Compare commits
37 Commits
work/tobia
...
work/jz/fl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae155805e9 | ||
|
|
70bff21632 | ||
|
|
f58c390a47 | ||
|
|
089a9abcb4 | ||
|
|
bf1c76d0a6 | ||
|
|
879da627b1 | ||
|
|
9b93eb44d5 | ||
|
|
b30220eca9 | ||
|
|
d270d4e5e1 | ||
|
|
21da6cb0f4 | ||
|
|
6ac75df935 | ||
|
|
f29781349c | ||
|
|
bb776d5c2b | ||
|
|
6cfab9e3ea | ||
|
|
6373186c15 | ||
|
|
e342de3bc1 | ||
|
|
4cd7b69ea5 | ||
|
|
988e8529da | ||
|
|
6a32d1e961 | ||
|
|
0552c798fb | ||
|
|
a53ad41879 | ||
|
|
92351edcd0 | ||
|
|
878eb48cb0 | ||
|
|
053ca6bed8 | ||
|
|
78ae14ab2f | ||
|
|
5fdc2ad765 | ||
|
|
b75dbe8d5c | ||
|
|
eaf4663c84 | ||
|
|
64b8cd5bcc | ||
|
|
482d61ee47 | ||
|
|
276dcce95e | ||
|
|
217f9e2e02 | ||
|
|
f40a0a6f5f | ||
|
|
9bd67acc2f | ||
|
|
e87da0feb0 | ||
|
|
2608d879fa | ||
|
|
6ab61fd41f |
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.6",
|
||||
"runtime-version": "6.7",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
|
||||
653
po/ar/neochat.po
653
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
644
po/az/neochat.po
644
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
637
po/ca/neochat.po
637
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
645
po/cs/neochat.po
645
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
628
po/da/neochat.po
628
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
637
po/de/neochat.po
637
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
646
po/el/neochat.po
646
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
644
po/eo/neochat.po
644
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
636
po/es/neochat.po
636
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
642
po/eu/neochat.po
642
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
633
po/fi/neochat.po
633
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
640
po/fr/neochat.po
640
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/hu/neochat.po
625
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
635
po/ia/neochat.po
635
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
637
po/id/neochat.po
637
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
628
po/ie/neochat.po
628
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
641
po/it/neochat.po
641
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
584
po/ja/neochat.po
584
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
635
po/ka/neochat.po
635
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
629
po/ko/neochat.po
629
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
596
po/lt/neochat.po
596
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
629
po/lv/neochat.po
629
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
636
po/nl/neochat.po
636
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
638
po/nn/neochat.po
638
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
645
po/pa/neochat.po
645
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
659
po/pl/neochat.po
659
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
637
po/pt/neochat.po
637
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
640
po/ru/neochat.po
640
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
647
po/sk/neochat.po
647
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
648
po/sl/neochat.po
648
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
631
po/sv/neochat.po
631
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
650
po/ta/neochat.po
650
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
637
po/tr/neochat.po
637
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
646
po/uk/neochat.po
646
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -172,9 +172,16 @@ add_library(neochat STATIC
|
||||
models/statekeysmodel.h
|
||||
sharehandler.cpp
|
||||
sharehandler.h
|
||||
models/roomtreeitem.cpp
|
||||
models/roomtreeitem.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE TRUE
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||
QML_FILES
|
||||
qml/main.qml
|
||||
qml/AccountMenu.qml
|
||||
@@ -205,8 +212,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/CompletionMenu.qml
|
||||
qml/PieProgressBar.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/RoomData.qml
|
||||
qml/ServerData.qml
|
||||
qml/EmojiPicker.qml
|
||||
qml/LoginStep.qml
|
||||
qml/Login.qml
|
||||
@@ -240,7 +245,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/FileDelegateContextMenu.qml
|
||||
qml/MessageSourceSheet.qml
|
||||
qml/ReportSheet.qml
|
||||
qml/DevtoolsPage.qml
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RemoveSheet.qml
|
||||
qml/BanSheet.qml
|
||||
@@ -280,14 +284,13 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/RoomTreeSection.qml
|
||||
qml/DelegateContextMenu.qml
|
||||
qml/ShareDialog.qml
|
||||
qml/FeatureFlagPage.qml
|
||||
qml/AccountData.qml
|
||||
qml/StateKeys.qml
|
||||
qml/UnlockSSSSDialog.qml
|
||||
qml/QrScannerPage.qml
|
||||
qml/JoinRoomDialog.qml
|
||||
qml/ConfirmUrlDialog.qml
|
||||
qml/AccountSwitchDialog.qml
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
@@ -295,6 +298,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(devtools)
|
||||
|
||||
if(UNIX)
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||
@@ -386,7 +390,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
|
||||
@@ -318,7 +318,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
updateBadgeNotificationCount(m_connection, m_connection->badgeNotificationCount());
|
||||
}
|
||||
|
||||
Q_EMIT activeConnectionChanged();
|
||||
Q_EMIT activeConnectionChanged(m_connection);
|
||||
}
|
||||
|
||||
void Controller::listenForNotifications()
|
||||
|
||||
@@ -129,6 +129,6 @@ Q_SIGNALS:
|
||||
void errorOccured(const QString &error, const QString &detail);
|
||||
void connectionAdded(NeoChatConnection *connection);
|
||||
void connectionDropped(NeoChatConnection *connection);
|
||||
void activeConnectionChanged();
|
||||
void activeConnectionChanged(NeoChatConnection *connection);
|
||||
void accountsLoadingChanged();
|
||||
};
|
||||
|
||||
15
src/devtools/CMakeLists.txt
Normal file
15
src/devtools/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(devtools STATIC)
|
||||
qt_add_qml_module(devtools
|
||||
URI org.kde.neochat.devtools
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/devtools
|
||||
QML_FILES
|
||||
DevtoolsPage.qml
|
||||
AccountData.qml
|
||||
FeatureFlagPage.qml
|
||||
RoomData.qml
|
||||
ServerData.qml
|
||||
StateKeys.qml
|
||||
)
|
||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.config
|
||||
|
||||
FormCard.FormCardPage {
|
||||
@@ -25,14 +25,11 @@ ColumnLayout {
|
||||
text: i18n("Room")
|
||||
textRole: "escapedDisplayName"
|
||||
valueRole: "roomId"
|
||||
displayText: roomListModel.data(roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
|
||||
model: RoomListModel {
|
||||
id: roomListModel
|
||||
connection: root.connection
|
||||
}
|
||||
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
|
||||
model: RoomManager.roomListModel
|
||||
currentIndex: 0
|
||||
Component.onCompleted: currentIndex = roomListModel.rowForRoom(root.room)
|
||||
onCurrentValueChanged: root.room = roomListModel.roomByAliasOrId(roomComboBox.currentValue)
|
||||
Component.onCompleted: currentIndex = RoomManager.roomListModel.rowForRoom(root.room)
|
||||
onCurrentValueChanged: root.room = RoomManager.roomListModel.roomByAliasOrId(roomComboBox.currentValue)
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18n("Room Id: %1", root.room.id)
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
Deprioritized, /**< The room is set as low priority. */
|
||||
Space, /**< The room is a space. */
|
||||
AddDirect, /**< So we can show the add friend delegate. */
|
||||
TypesCount, /**< Number of different types (this should always be last). */
|
||||
};
|
||||
Q_ENUM(Types);
|
||||
|
||||
|
||||
@@ -230,6 +230,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_settingsPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_timelinePlugin)
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_devtoolsPlugin)
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
|
||||
|
||||
@@ -161,7 +161,7 @@ QVariant CustomEmojiModel::data(const QModelIndex &idx, int role) const
|
||||
case Roles::ImageURL:
|
||||
return QUrl(QStringLiteral("image://mxc/") + data.url.mid(6));
|
||||
case Roles::MxcUrl:
|
||||
return data.url.mid(6);
|
||||
return m_connection->makeMediaUrl(QUrl(data.url));
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,15 @@ using namespace Quotient;
|
||||
DevicesModel::DevicesModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &, const QString &deviceId) {
|
||||
const auto it = std::find_if(m_devices.begin(), m_devices.end(), [deviceId](const Quotient::Device &device) {
|
||||
return device.deviceId == deviceId;
|
||||
});
|
||||
if (it != m_devices.end()) {
|
||||
const auto index = this->index(it - m_devices.begin());
|
||||
Q_EMIT dataChanged(index, index, {Type});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DevicesModel::fetchDevices()
|
||||
|
||||
@@ -124,6 +124,15 @@ void PublicRoomListModel::setShowOnlySpaces(bool showOnlySpaces)
|
||||
}
|
||||
m_showOnlySpaces = showOnlySpaces;
|
||||
Q_EMIT showOnlySpacesChanged();
|
||||
|
||||
nextBatch = QString();
|
||||
attempted = false;
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PublicRoomListModel::search(int limit)
|
||||
@@ -243,6 +252,9 @@ QVariant PublicRoomListModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
return m_connection->room(room.roomId, JoinState::Join) != nullptr;
|
||||
}
|
||||
if (role == IsSpaceRole) {
|
||||
return room.roomType == QLatin1String("m.space");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -259,6 +271,7 @@ QHash<int, QByteArray> PublicRoomListModel::roleNames() const
|
||||
roles[AllowGuestsRole] = "allowGuests";
|
||||
roles[WorldReadableRole] = "worldReadable";
|
||||
roles[IsJoinedRole] = "isJoined";
|
||||
roles[IsSpaceRole] = "isSpace";
|
||||
roles[AliasRole] = "alias";
|
||||
|
||||
return roles;
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
AllowGuestsRole, /**< Whether the room allows guest users. */
|
||||
WorldReadableRole, /**< Whether the room events can be seen by non-members. */
|
||||
IsJoinedRole, /**< Whether the local user has joined the room. */
|
||||
IsSpaceRole, /**< Whether the room is a space. */
|
||||
};
|
||||
|
||||
explicit PublicRoomListModel(QObject *parent = nullptr);
|
||||
|
||||
100
src/models/roomtreeitem.cpp
Normal file
100
src/models/roomtreeitem.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
|
||||
// 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
|
||||
|
||||
#include "roomtreeitem.h"
|
||||
|
||||
RoomTreeItem::RoomTreeItem(TreeData data, RoomTreeItem *parent)
|
||||
: m_parentItem(parent)
|
||||
, m_data(data)
|
||||
{
|
||||
}
|
||||
|
||||
bool RoomTreeItem::operator==(const RoomTreeItem &other) const
|
||||
{
|
||||
if (std::holds_alternative<NeoChatRoomType::Types>(m_data) && std::holds_alternative<NeoChatRoomType::Types>(other.data())) {
|
||||
return std::get<NeoChatRoomType::Types>(m_data) == std::get<NeoChatRoomType::Types>(m_data);
|
||||
}
|
||||
if (std::holds_alternative<NeoChatRoom *>(m_data) && std::holds_alternative<NeoChatRoom *>(other.data())) {
|
||||
return std::get<NeoChatRoom *>(m_data)->id() == std::get<NeoChatRoom *>(m_data)->id();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomTreeItem *RoomTreeItem::child(int row)
|
||||
{
|
||||
return row >= 0 && row < childCount() ? m_children.at(row).get() : nullptr;
|
||||
}
|
||||
|
||||
int RoomTreeItem::childCount() const
|
||||
{
|
||||
return int(m_children.size());
|
||||
}
|
||||
|
||||
bool RoomTreeItem::insertChild(std::unique_ptr<RoomTreeItem> newChild)
|
||||
{
|
||||
if (newChild == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto it = m_children.begin(), end = m_children.end(); it != end; ++it) {
|
||||
if (*it == newChild) {
|
||||
*it = std::move(newChild);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_children.push_back(std::move(newChild));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RoomTreeItem::removeChild(int row)
|
||||
{
|
||||
if (row < 0 || row >= childCount()) {
|
||||
return false;
|
||||
}
|
||||
m_children.erase(m_children.begin() + row);
|
||||
return true;
|
||||
}
|
||||
|
||||
int RoomTreeItem::row() const
|
||||
{
|
||||
if (m_parentItem == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto it = std::find_if(m_parentItem->m_children.cbegin(), m_parentItem->m_children.cend(), [this](const std::unique_ptr<RoomTreeItem> &treeItem) {
|
||||
return treeItem.get() == this;
|
||||
});
|
||||
|
||||
if (it != m_parentItem->m_children.cend()) {
|
||||
return std::distance(m_parentItem->m_children.cbegin(), it);
|
||||
}
|
||||
Q_ASSERT(false); // should not happen
|
||||
return -1;
|
||||
}
|
||||
|
||||
RoomTreeItem *RoomTreeItem::parentItem() const
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
|
||||
RoomTreeItem::TreeData RoomTreeItem::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
std::optional<int> RoomTreeItem::rowForRoom(Quotient::Room *room) const
|
||||
{
|
||||
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Types>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
|
||||
|
||||
int i = 0;
|
||||
for (const auto &child : m_children) {
|
||||
if (std::get<NeoChatRoom *>(child->data()) == room) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
78
src/models/roomtreeitem.h
Normal file
78
src/models/roomtreeitem.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
|
||||
// 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
|
||||
|
||||
#include "enums/neochatroomtype.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class RoomTreeItem
|
||||
*
|
||||
* This class defines an item in the space tree hierarchy model.
|
||||
*
|
||||
* @note This is separate from Quotient::Room and NeoChatRoom because we don't have
|
||||
* full room information for any room/space the user hasn't joined and we
|
||||
* don't want to create one for ever possible child in a space as that would
|
||||
* be expensive.
|
||||
*
|
||||
* @sa Quotient::Room, NeoChatRoom
|
||||
*/
|
||||
class RoomTreeItem
|
||||
{
|
||||
public:
|
||||
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
|
||||
|
||||
explicit RoomTreeItem(TreeData data, RoomTreeItem *parent = nullptr);
|
||||
|
||||
bool operator==(const RoomTreeItem &other) const;
|
||||
|
||||
/**
|
||||
* @brief Return the child at the given row number.
|
||||
*
|
||||
* Nullptr is returned if there is no child at the given row number.
|
||||
*/
|
||||
RoomTreeItem *child(int row);
|
||||
|
||||
/**
|
||||
* @brief The number of children this item has.
|
||||
*/
|
||||
int childCount() const;
|
||||
|
||||
/**
|
||||
* @brief Insert the given child.
|
||||
*/
|
||||
bool insertChild(std::unique_ptr<RoomTreeItem> newChild);
|
||||
|
||||
/**
|
||||
* @brief Remove the child at the given row number.
|
||||
*
|
||||
* @return True if a child was removed, false if the given row isn't valid.
|
||||
*/
|
||||
bool removeChild(int row);
|
||||
|
||||
/**
|
||||
* @brief Return this item's parent.
|
||||
*/
|
||||
RoomTreeItem *parentItem() const;
|
||||
|
||||
/**
|
||||
* @brief Return the row number for this child relative to the parent.
|
||||
*
|
||||
* @return The row value if the child has a parent, 0 otherwise.
|
||||
*/
|
||||
int row() const;
|
||||
|
||||
/**
|
||||
* @brief Return this item's data.
|
||||
*/
|
||||
TreeData data() const;
|
||||
|
||||
std::optional<int> rowForRoom(Quotient::Room *room) const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<RoomTreeItem>> m_children;
|
||||
RoomTreeItem *m_parentItem;
|
||||
|
||||
TreeData m_data;
|
||||
};
|
||||
@@ -15,21 +15,47 @@ using namespace Quotient;
|
||||
|
||||
RoomTreeModel::RoomTreeModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_rootItem(new RoomTreeItem(nullptr))
|
||||
{
|
||||
initializeCategories();
|
||||
}
|
||||
|
||||
void RoomTreeModel::initializeCategories()
|
||||
RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
|
||||
{
|
||||
for (const auto &key : m_rooms.keys()) {
|
||||
for (const auto &room : m_rooms[key]) {
|
||||
room->disconnect(this);
|
||||
if (index.isValid()) {
|
||||
RoomTreeItem *item = static_cast<RoomTreeItem *>(index.internalPointer());
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
m_rooms.clear();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
m_rooms[NeoChatRoomType::Types(i)] = {};
|
||||
return m_rootItem.get();
|
||||
}
|
||||
|
||||
void RoomTreeModel::resetModel()
|
||||
{
|
||||
if (m_connection == nullptr) {
|
||||
beginResetModel();
|
||||
m_rootItem.reset();
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
m_rootItem.reset(new RoomTreeItem(nullptr));
|
||||
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
m_rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
|
||||
}
|
||||
|
||||
for (const auto &r : m_connection->allRooms()) {
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
const auto categoryItem = m_rootItem->child(type);
|
||||
if (categoryItem->insertChild(std::make_unique<RoomTreeItem>(room, categoryItem))) {
|
||||
connectRoomSignals(room);
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void RoomTreeModel::setConnection(NeoChatConnection *connection)
|
||||
@@ -41,16 +67,13 @@ void RoomTreeModel::setConnection(NeoChatConnection *connection)
|
||||
disconnect(m_connection.get(), nullptr, this, nullptr);
|
||||
}
|
||||
m_connection = connection;
|
||||
beginResetModel();
|
||||
initializeCategories();
|
||||
endResetModel();
|
||||
|
||||
resetModel();
|
||||
|
||||
connect(connection, &Connection::newRoom, this, &RoomTreeModel::newRoom);
|
||||
connect(connection, &Connection::leftRoom, this, &RoomTreeModel::leftRoom);
|
||||
connect(connection, &Connection::aboutToDeleteRoom, this, &RoomTreeModel::leftRoom);
|
||||
|
||||
for (const auto &room : m_connection->allRooms()) {
|
||||
newRoom(dynamic_cast<NeoChatRoom *>(room));
|
||||
}
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
@@ -68,23 +91,28 @@ void RoomTreeModel::newRoom(Room *r)
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(index(type, 0), m_rooms[type].size(), m_rooms[type].size());
|
||||
m_rooms[type].append(room);
|
||||
const auto parentItem = m_rootItem->child(type);
|
||||
beginInsertRows(index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
parentItem->insertChild(std::make_unique<RoomTreeItem>(room, parentItem));
|
||||
connectRoomSignals(room);
|
||||
endInsertRows();
|
||||
qWarning() << "adding room" << type << "new count" << parentItem->childCount();
|
||||
}
|
||||
|
||||
void RoomTreeModel::leftRoom(Room *r)
|
||||
{
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
auto row = m_rooms[type].indexOf(room);
|
||||
if (row == -1) {
|
||||
auto index = indexForRoom(room);
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
beginRemoveRows(index(type, 0), row, row);
|
||||
m_rooms[type][row]->disconnect(this);
|
||||
m_rooms[type].removeAt(row);
|
||||
|
||||
const auto parentItem = getItem(index.parent());
|
||||
Q_ASSERT(parentItem);
|
||||
|
||||
beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
parentItem->removeChild(index.row());
|
||||
room->disconnect(this);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
@@ -94,30 +122,41 @@ void RoomTreeModel::moveRoom(Quotient::Room *room)
|
||||
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
|
||||
NeoChatRoomType::Types oldType;
|
||||
int oldRow = -1;
|
||||
for (const auto &key : m_rooms.keys()) {
|
||||
if (m_rooms[key].contains(room)) {
|
||||
oldType = key;
|
||||
oldRow = m_rooms[key].indexOf(room);
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
const auto categoryItem = m_rootItem->child(i);
|
||||
const auto row = categoryItem->rowForRoom(room);
|
||||
if (row) {
|
||||
oldType = static_cast<NeoChatRoomType::Types>(i);
|
||||
oldRow = *row;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldRow == -1) {
|
||||
return;
|
||||
}
|
||||
const auto newType = NeoChatRoomType::typeForRoom(dynamic_cast<NeoChatRoom *>(room));
|
||||
auto neochatRoom = dynamic_cast<NeoChatRoom *>(room);
|
||||
const auto newType = NeoChatRoomType::typeForRoom(neochatRoom);
|
||||
if (newType == oldType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto oldParent = index(oldType, 0, {});
|
||||
auto oldParentItem = getItem(oldParent);
|
||||
Q_ASSERT(oldParentItem);
|
||||
|
||||
const auto newParent = index(newType, 0, {});
|
||||
auto newParentItem = getItem(newParent);
|
||||
Q_ASSERT(newParentItem);
|
||||
|
||||
// HACK: We're doing this as a remove then insert because moving doesn't work
|
||||
// properly with DelegateChooser for whatever reason.
|
||||
Q_ASSERT(checkIndex(index(oldRow, 0, oldParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||
beginRemoveRows(oldParent, oldRow, oldRow);
|
||||
m_rooms[oldType].removeAt(oldRow);
|
||||
const bool success = oldParentItem->removeChild(oldRow);
|
||||
Q_ASSERT(success);
|
||||
endRemoveRows();
|
||||
beginInsertRows(newParent, m_rooms[newType].size(), m_rooms[newType].size());
|
||||
m_rooms[newType].append(dynamic_cast<NeoChatRoom *>(room));
|
||||
beginInsertRows(newParent, newParentItem->childCount(), newParentItem->childCount());
|
||||
newParentItem->insertChild(std::make_unique<RoomTreeItem>(neochatRoom, newParentItem));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@@ -151,15 +190,12 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
||||
|
||||
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
|
||||
{
|
||||
const auto roomType = NeoChatRoomType::typeForRoom(room);
|
||||
const auto it = std::find(m_rooms[roomType].begin(), m_rooms[roomType].end(), room);
|
||||
if (it == m_rooms[roomType].end()) {
|
||||
const auto index = indexForRoom(room);
|
||||
if (!index.isValid()) {
|
||||
qCritical() << "Room" << room->id() << "not found in the room list";
|
||||
return;
|
||||
}
|
||||
const auto parentIndex = index(roomType, 0, {});
|
||||
const auto idx = index(it - m_rooms[roomType].begin(), 0, parentIndex);
|
||||
Q_EMIT dataChanged(idx, idx, roles);
|
||||
Q_EMIT dataChanged(index, index, roles);
|
||||
}
|
||||
|
||||
NeoChatConnection *RoomTreeModel::connection() const
|
||||
@@ -175,32 +211,55 @@ int RoomTreeModel::columnCount(const QModelIndex &parent) const
|
||||
|
||||
int RoomTreeModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
RoomTreeItem *parentItem;
|
||||
if (parent.column() > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!parent.isValid()) {
|
||||
return m_rooms.keys().size();
|
||||
parentItem = m_rootItem.get();
|
||||
} else {
|
||||
parentItem = static_cast<RoomTreeItem *>(parent.internalPointer());
|
||||
}
|
||||
if (!parent.parent().isValid()) {
|
||||
return m_rooms.values()[parent.row()].size();
|
||||
}
|
||||
return 0;
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.internalPointer()) {
|
||||
return {};
|
||||
if (!index.isValid()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
return this->index(NeoChatRoomType::typeForRoom(static_cast<NeoChatRoom *>(index.internalPointer())), 0, QModelIndex());
|
||||
|
||||
RoomTreeItem *childItem = static_cast<RoomTreeItem *>(index.internalPointer());
|
||||
if (!childItem) {
|
||||
return QModelIndex();
|
||||
}
|
||||
RoomTreeItem *parentItem = childItem->parentItem();
|
||||
|
||||
if (parentItem == m_rootItem.get()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
QModelIndex RoomTreeModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid()) {
|
||||
return createIndex(row, column, nullptr);
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
if (row >= rowCount(parent)) {
|
||||
return {};
|
||||
|
||||
RoomTreeItem *parentItem = getItem(parent);
|
||||
if (!parentItem) {
|
||||
return QModelIndex();
|
||||
}
|
||||
return createIndex(row, column, m_rooms[NeoChatRoomType::Types(parent.row())][row]);
|
||||
|
||||
RoomTreeItem *childItem = parentItem->child(row);
|
||||
if (childItem) {
|
||||
return createIndex(row, column, childItem);
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
||||
@@ -235,7 +294,8 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (!index.parent().isValid()) {
|
||||
RoomTreeItem *child = getItem(index);
|
||||
if (std::holds_alternative<NeoChatRoomType::Types>(child->data())) {
|
||||
if (role == DisplayNameRole) {
|
||||
return NeoChatRoomType::typeName(index.row());
|
||||
}
|
||||
@@ -256,7 +316,8 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
return {};
|
||||
}
|
||||
const auto room = m_rooms.values()[index.parent().row()][index.row()].get();
|
||||
|
||||
const auto room = std::get<NeoChatRoom *>(child->data());
|
||||
Q_ASSERT(room);
|
||||
|
||||
if (role == DisplayNameRole) {
|
||||
@@ -338,16 +399,20 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
||||
|
||||
// Try and find by checking type.
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
auto row = m_rooms[type].indexOf(room);
|
||||
if (row >= 0) {
|
||||
return index(row, 0, index(type, 0));
|
||||
const auto parentItem = m_rootItem->child(type);
|
||||
const auto row = parentItem->rowForRoom(room);
|
||||
if (row) {
|
||||
return index(*row, 0, index(type, 0));
|
||||
}
|
||||
// Double check that the room isn't in the wrong category.
|
||||
for (const auto &key : m_rooms.keys()) {
|
||||
if (m_rooms[key].contains(room)) {
|
||||
return index(m_rooms[key].indexOf(room), 0, index(key, 0));
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
const auto parentItem = m_rootItem->child(i);
|
||||
const auto row = parentItem->rowForRoom(room);
|
||||
if (row) {
|
||||
return index(*row, 0, index(i, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QPointer>
|
||||
|
||||
#include "enums/neochatroomtype.h"
|
||||
#include "roomtreeitem.h"
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
@@ -82,10 +83,12 @@ Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection = nullptr;
|
||||
QMap<NeoChatRoomType::Types, QList<QPointer<NeoChatRoom>>> m_rooms;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
std::unique_ptr<RoomTreeItem> m_rootItem;
|
||||
|
||||
void initializeCategories();
|
||||
RoomTreeItem *getItem(const QModelIndex &index) const;
|
||||
|
||||
void resetModel();
|
||||
void connectRoomSignals(NeoChatRoom *room);
|
||||
|
||||
void newRoom(Quotient::Room *room);
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
|
||||
#include "roomlistmodel.h"
|
||||
|
||||
SortFilterRoomListModel::SortFilterRoomListModel(QObject *parent)
|
||||
SortFilterRoomListModel::SortFilterRoomListModel(RoomListModel *sourceModel, QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
Q_ASSERT(sourceModel);
|
||||
setSourceModel(sourceModel);
|
||||
|
||||
sort(0);
|
||||
invalidateFilter();
|
||||
connect(this, &SortFilterRoomListModel::filterTextChanged, this, [this]() {
|
||||
invalidateFilter();
|
||||
});
|
||||
connect(this, &SortFilterRoomListModel::sourceModelChanged, this, [this]() {
|
||||
connect(sourceModel(), &QAbstractListModel::rowsInserted, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
connect(sourceModel(), &QAbstractListModel::rowsRemoved, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
connect(this->sourceModel(), &QAbstractListModel::rowsInserted, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
connect(this->sourceModel(), &QAbstractListModel::rowsRemoved, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "models/roomlistmodel.h"
|
||||
|
||||
/**
|
||||
* @class SortFilterRoomListModel
|
||||
*
|
||||
@@ -29,6 +31,7 @@ class SortFilterRoomListModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The text to use to filter room names.
|
||||
@@ -36,7 +39,7 @@ class SortFilterRoomListModel : public QSortFilterProxyModel
|
||||
Q_PROPERTY(QString filterText READ filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged)
|
||||
|
||||
public:
|
||||
explicit SortFilterRoomListModel(QObject *parent = nullptr);
|
||||
explicit SortFilterRoomListModel(RoomListModel *sourceModel, QObject *parent = nullptr);
|
||||
|
||||
void setFilterText(const QString &text);
|
||||
[[nodiscard]] QString filterText() const;
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
#include "roomtreemodel.h"
|
||||
#include "spacehierarchycache.h"
|
||||
|
||||
SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
|
||||
SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
Q_ASSERT(sourceModel);
|
||||
setSourceModel(sourceModel);
|
||||
|
||||
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
|
||||
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||
@@ -21,12 +24,11 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
|
||||
|
||||
setRecursiveFilteringEnabled(true);
|
||||
sort(0);
|
||||
invalidateFilter();
|
||||
connect(this, &SortFilterRoomTreeModel::filterTextChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
connect(this, &SortFilterRoomTreeModel::sourceModelChanged, this, [this]() {
|
||||
sourceModel()->disconnect(this);
|
||||
connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
this->sourceModel()->disconnect(this);
|
||||
connect(this->sourceModel(), &QAbstractItemModel::rowsInserted, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
connect(this->sourceModel(), &QAbstractItemModel::rowsRemoved, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
});
|
||||
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
|
||||
@@ -32,6 +32,7 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The text to use to filter room names.
|
||||
@@ -62,7 +63,7 @@ public:
|
||||
};
|
||||
Q_ENUM(Mode)
|
||||
|
||||
explicit SortFilterRoomTreeModel(QObject *parent = nullptr);
|
||||
explicit SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent = nullptr);
|
||||
|
||||
void setRoomSortOrder(RoomSortOrder sortOrder);
|
||||
|
||||
|
||||
@@ -5,22 +5,21 @@
|
||||
|
||||
#include "roomlistmodel.h"
|
||||
|
||||
SortFilterSpaceListModel::SortFilterSpaceListModel(QObject *parent)
|
||||
SortFilterSpaceListModel::SortFilterSpaceListModel(RoomListModel *sourceModel, QObject *parent)
|
||||
: QSortFilterProxyModel{parent}
|
||||
{
|
||||
setSortRole(RoomListModel::RoomIdRole);
|
||||
sort(0);
|
||||
invalidateFilter();
|
||||
connect(this, &QAbstractProxyModel::sourceModelChanged, this, [this]() {
|
||||
connect(sourceModel(), &QAbstractListModel::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, QList<int> roles) {
|
||||
if (roles.contains(RoomListModel::IsChildSpaceRole)) {
|
||||
invalidate();
|
||||
}
|
||||
countChanged();
|
||||
});
|
||||
invalidate();
|
||||
Q_ASSERT(sourceModel);
|
||||
setSourceModel(sourceModel);
|
||||
|
||||
connect(this->sourceModel(), &QAbstractListModel::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, QList<int> roles) {
|
||||
if (roles.contains(RoomListModel::IsChildSpaceRole)) {
|
||||
invalidate();
|
||||
}
|
||||
Q_EMIT countChanged();
|
||||
});
|
||||
|
||||
setSortRole(RoomListModel::RoomIdRole);
|
||||
sort(0);
|
||||
}
|
||||
|
||||
bool SortFilterSpaceListModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "models/roomlistmodel.h"
|
||||
|
||||
/**
|
||||
* @class SortFilterSpaceListModel
|
||||
*
|
||||
@@ -18,6 +20,7 @@ class SortFilterSpaceListModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The number of spaces in the model.
|
||||
@@ -25,7 +28,7 @@ class SortFilterSpaceListModel : public QSortFilterProxyModel
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
|
||||
public:
|
||||
explicit SortFilterSpaceListModel(QObject *parent = nullptr);
|
||||
explicit SortFilterSpaceListModel(RoomListModel *sourceModel, QObject *parent = nullptr);
|
||||
|
||||
Q_SIGNALS:
|
||||
void countChanged();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"Name": "Tobias Fella",
|
||||
"Name[ca@valencia]": "Tobias Fella",
|
||||
"Name[ca]": "Tobias Fella",
|
||||
"Name[cs]": "Tobias Fella",
|
||||
"Name[de]": "Tobias Fella",
|
||||
"Name[es]": "Tobias Fella",
|
||||
"Name[eu]": "Tobias Fella",
|
||||
@@ -49,6 +50,7 @@
|
||||
"Name[ast]": "NeoChat",
|
||||
"Name[ca@valencia]": "NeoChat",
|
||||
"Name[ca]": "NeoChat",
|
||||
"Name[cs]": "NeoChat",
|
||||
"Name[de]": "NeoChat",
|
||||
"Name[es]": "NeoChat",
|
||||
"Name[eu]": "NeoChat",
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.devtools
|
||||
import org.kde.neochat.config
|
||||
|
||||
QQC2.Menu {
|
||||
@@ -55,7 +56,7 @@ QQC2.Menu {
|
||||
text: i18n("Open developer tools")
|
||||
icon.name: "tools"
|
||||
visible: Config.developerTools
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'DevtoolsPage.qml'), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title:window", "Developer Tools"),
|
||||
|
||||
@@ -18,16 +18,11 @@ Kirigami.ScrollablePage {
|
||||
required property NeoChatConnection connection
|
||||
|
||||
header: Kirigami.SearchField {
|
||||
onTextChanged: sortModel.filterText = text
|
||||
onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text
|
||||
}
|
||||
|
||||
ListView {
|
||||
model: SortFilterRoomListModel {
|
||||
id: sortModel
|
||||
sourceModel: RoomListModel {
|
||||
connection: root.connection
|
||||
}
|
||||
}
|
||||
model: RoomManager.sortFilterRoomListModel
|
||||
delegate: RoomDelegate {
|
||||
id: roomDelegate
|
||||
onClicked: {
|
||||
@@ -36,4 +31,6 @@ Kirigami.ScrollablePage {
|
||||
connection: root.connection
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: Qt.callLater(() => header.forceActiveFocus())
|
||||
}
|
||||
|
||||
169
src/qml/CodeMaximizeComponent.qml
Normal file
169
src/qml/CodeMaximizeComponent.qml
Normal file
@@ -0,0 +1,169 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigamiaddons.labs.components as Components
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.syntaxhighlighting
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Components.AbstractMaximizeComponent {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The message author.
|
||||
*
|
||||
* This should consist of the following:
|
||||
* - id - The matrix ID of the author.
|
||||
* - isLocalUser - Whether the author is the local user.
|
||||
* - avatarSource - The mxc URL for the author's avatar in the current room.
|
||||
* - avatarMediaId - The media ID of the author's avatar.
|
||||
* - avatarUrl - The mxc URL for the author's avatar.
|
||||
* - displayName - The display name of the author.
|
||||
* - display - The name of the author.
|
||||
* - color - The color for the author.
|
||||
* - object - The Quotient::User object for the author.
|
||||
*
|
||||
* @sa Quotient::User
|
||||
*/
|
||||
property var author
|
||||
|
||||
/**
|
||||
* @brief The timestamp of the message.
|
||||
*/
|
||||
property var time
|
||||
|
||||
/**
|
||||
* @brief The code text to show.
|
||||
*/
|
||||
property string codeText
|
||||
|
||||
/**
|
||||
* @brief The code language, if any.
|
||||
*/
|
||||
property string language
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action", "Copy to clipboard")
|
||||
icon.name: "edit-copy"
|
||||
onTriggered: Clipboard.saveText(root.codeText)
|
||||
}
|
||||
]
|
||||
|
||||
leading: RowLayout {
|
||||
Components.Avatar {
|
||||
id: userAvatar
|
||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||
|
||||
name: root.author.name ?? root.author.displayName
|
||||
source: root.author.avatarSource
|
||||
color: root.author.color
|
||||
}
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
QQC2.Label {
|
||||
id: userLabel
|
||||
|
||||
text: root.author.name ?? root.author.displayName
|
||||
color: root.author.color
|
||||
font.weight: Font.Bold
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
QQC2.Label {
|
||||
id: dateTimeLabel
|
||||
text: root.time.toLocaleString(Qt.locale(), Locale.ShortFormat)
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content: QQC2.ScrollView {
|
||||
id: codeScrollView
|
||||
contentWidth: root.width
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
QQC2.TextArea {
|
||||
id: codeText
|
||||
topPadding: Kirigami.Units.smallSpacing
|
||||
bottomPadding: Kirigami.Units.smallSpacing
|
||||
leftPadding: lineNumberColumn.width + lineNumberColumn.anchors.leftMargin + Kirigami.Units.smallSpacing * 2
|
||||
|
||||
text: root.codeText
|
||||
readOnly: true
|
||||
textFormat: TextEdit.PlainText
|
||||
wrapMode: TextEdit.Wrap
|
||||
color: Kirigami.Theme.textColor
|
||||
|
||||
font.family: "monospace"
|
||||
|
||||
Kirigami.SpellCheck.enabled: false
|
||||
|
||||
onWidthChanged: lineModel.resetModel()
|
||||
onHeightChanged: lineModel.resetModel()
|
||||
|
||||
SyntaxHighlighter {
|
||||
property string definitionName: Repository.definitionForName(root.language).name
|
||||
textEdit: definitionName == "None" ? null : codeText
|
||||
definition: definitionName
|
||||
}
|
||||
ColumnLayout {
|
||||
id: lineNumberColumn
|
||||
anchors {
|
||||
top: codeText.top
|
||||
topMargin: codeText.topPadding + 1
|
||||
left: codeText.left
|
||||
leftMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
spacing: 0
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: LineModel {
|
||||
id: lineModel
|
||||
document: codeText.textDocument
|
||||
}
|
||||
delegate: QQC2.Label {
|
||||
id: label
|
||||
required property int index
|
||||
required property int docLineHeight
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: docLineHeight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: index + 1
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
|
||||
font.family: "monospace"
|
||||
}
|
||||
}
|
||||
}
|
||||
Kirigami.Separator {
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
leftMargin: lineNumberColumn.width + lineNumberColumn.anchors.leftMargin + Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: root.close()
|
||||
}
|
||||
|
||||
background: null
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,8 @@ QQC2.Popup {
|
||||
root.open();
|
||||
}
|
||||
|
||||
RoomListModel {
|
||||
id: roomListModel
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
chatDocumentHandler.completionModel.roomListModel = roomListModel;
|
||||
chatDocumentHandler.completionModel.roomListModel = RoomManager.roomListModel;
|
||||
}
|
||||
|
||||
function incrementIndex() {
|
||||
|
||||
32
src/qml/ConfirmLeaveDialog.qml
Normal file
32
src/qml/ConfirmLeaveDialog.qml
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom room
|
||||
|
||||
width: Kirigami.Units.gridUnit * 24
|
||||
|
||||
title: i18nc("@title:dialog", "Confirm Leaving Room")
|
||||
|
||||
contentItem: FormCard.FormTextDelegate {
|
||||
text: root.room ? i18nc("Do you really want to leave <room name>?", "Do you really want to leave %1?", root.room.displayName) : ""
|
||||
}
|
||||
|
||||
customFooterActions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:button", "Leave Room")
|
||||
icon.name: "arrow-left"
|
||||
onTriggered: RoomManager.leaveRoom(root.room)
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,11 +14,11 @@ Kirigami.Dialog {
|
||||
width: Kirigami.Units.gridUnit * 24
|
||||
height: Kirigami.Units.gridUnit * 8
|
||||
|
||||
title: i18nc("@title", "Open Url")
|
||||
standardButtons: Kirigami.Dialog.Yes | Kirigami.Dialog.No
|
||||
title: i18nc("@title", "Open URL")
|
||||
standardButtons: QQC2.DialogButtonBox.Open | QQC2.DialogButtonBox.Cancel
|
||||
|
||||
contentItem: QQC2.Label {
|
||||
text: i18nc("Do you want to open <link>", "Do you want to open <b>%1</b>?", root.link)
|
||||
text: xi18nc("@info", "Do you want to open <link>%1</link>?", root.link)
|
||||
wrapMode: QQC2.Label.Wrap
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
@@ -84,6 +84,7 @@ Loader {
|
||||
* Some common actions shared between menus
|
||||
*/
|
||||
component ViewSourceAction: Kirigami.Action {
|
||||
visible: Config.developerTools
|
||||
text: i18n("View Source")
|
||||
icon.name: "code-context"
|
||||
onTriggered: RoomManager.viewEventSource(root.eventId)
|
||||
|
||||
@@ -32,7 +32,13 @@ SearchPage {
|
||||
/**
|
||||
* @brief Whether results should only includes spaces.
|
||||
*/
|
||||
property bool showOnlySpaces: false
|
||||
property bool showOnlySpaces: spacesOnlyButton.checked
|
||||
onShowOnlySpacesChanged: updateSearch()
|
||||
|
||||
/**
|
||||
* @brief Whetherthe button to toggle the showOnlySpaces state should be shown.
|
||||
*/
|
||||
property bool showOnlySpacesButton: true
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a room is selected.
|
||||
@@ -47,9 +53,22 @@ SearchPage {
|
||||
|
||||
Component.onCompleted: focusSearch()
|
||||
|
||||
headerTrailing: ServerComboBox {
|
||||
id: serverComboBox
|
||||
connection: root.connection
|
||||
headerTrailing: RowLayout {
|
||||
QQC2.Button {
|
||||
id: spacesOnlyButton
|
||||
icon.name: "globe"
|
||||
display: QQC2.Button.IconOnly
|
||||
checkable: true
|
||||
text: i18nc("@action:button", "Only show spaces")
|
||||
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
ServerComboBox {
|
||||
id: serverComboBox
|
||||
connection: root.connection
|
||||
}
|
||||
}
|
||||
|
||||
model: PublicRoomListModel {
|
||||
|
||||
@@ -21,6 +21,7 @@ Delegates.RoundedItemDelegate {
|
||||
required property string topic
|
||||
required property int memberCount
|
||||
required property bool isJoined
|
||||
required property bool isSpace
|
||||
property bool justJoined: false
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ Delegates.RoundedItemDelegate {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillWidth: !spaceLabel.visible
|
||||
level: 4
|
||||
text: root.displayName
|
||||
font.bold: true
|
||||
@@ -64,6 +65,13 @@ Delegates.RoundedItemDelegate {
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
QQC2.Label {
|
||||
id: spaceLabel
|
||||
Layout.fillWidth: true
|
||||
visible: root.isSpace
|
||||
text: i18nc("@info:label A matrix space", "Space")
|
||||
color: Kirigami.Theme.linkColor
|
||||
}
|
||||
QQC2.Label {
|
||||
visible: root.isJoined || root.justJoined
|
||||
text: i18n("Joined")
|
||||
|
||||
@@ -47,10 +47,12 @@ ApplicationWindow {
|
||||
isLive: true
|
||||
heading: NaN
|
||||
visible: !isNaN(root.latitude) && !isNaN(root.longitude)
|
||||
Component.onCompleted: mapView.map.addMapItem(this)
|
||||
}
|
||||
MapItemView {
|
||||
model: root.liveLocationModel
|
||||
delegate: LocationMapItem {}
|
||||
Component.onCompleted: mapView.map.addMapItemView(this)
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
@@ -35,52 +35,25 @@ Components.AbstractMaximizeComponent {
|
||||
|
||||
content: MapView {
|
||||
id: mapView
|
||||
map.plugin: Plugin {
|
||||
name: "osm"
|
||||
PluginParameter {
|
||||
name: "osm.useragent"
|
||||
value: Application.name + "/" + Application.version + " (kde-devel@kde.org)"
|
||||
}
|
||||
PluginParameter {
|
||||
name: "osm.mapping.providersrepository.address"
|
||||
value: "https://autoconfig.kde.org/qtlocation/"
|
||||
}
|
||||
}
|
||||
map.plugin: OsmLocationPlugin.plugin
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.location = mapView.map.toCoordinate(Qt.point(mouseX, mouseY), false);
|
||||
mapView.map.addMapItem(mapView.locationMapItem);
|
||||
}
|
||||
}
|
||||
|
||||
MapQuickItem {
|
||||
id: point
|
||||
|
||||
visible: root.location
|
||||
anchorPoint.x: sourceItem.width / 2
|
||||
anchorPoint.y: sourceItem.height * 0.85
|
||||
coordinate: root.location
|
||||
autoFadeIn: false
|
||||
|
||||
sourceItem: Kirigami.Icon {
|
||||
width: height
|
||||
height: Kirigami.Units.iconSizes.huge
|
||||
source: "gps"
|
||||
isMask: true
|
||||
color: Kirigami.Theme.highlightColor
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -parent.height / 8
|
||||
width: height
|
||||
height: parent.height / 3 + 1
|
||||
source: "pin"
|
||||
isMask: true
|
||||
color: Kirigami.Theme.highlightColor
|
||||
}
|
||||
}
|
||||
readonly property LocationMapItem locationMapItem: LocationMapItem {
|
||||
latitude: root.location.latitude
|
||||
longitude: root.location.longitude
|
||||
isLive: false
|
||||
heading: NaN
|
||||
asset: ""
|
||||
author: null
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated() {
|
||||
|
||||
@@ -29,17 +29,22 @@ Kirigami.Page {
|
||||
map.zoomLevel: LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), mapView.width, mapView.height)
|
||||
|
||||
MapItemView {
|
||||
Component.onCompleted: mapView.map.addMapItemView(this)
|
||||
anchors.fill: parent
|
||||
|
||||
model: LocationsModel {
|
||||
id: locationsModel
|
||||
room: root.room
|
||||
}
|
||||
delegate: LocationMapItem {
|
||||
isLive: true
|
||||
isLive: false
|
||||
heading: NaN
|
||||
}
|
||||
}
|
||||
|
||||
MapItemView {
|
||||
Component.onCompleted: mapView.map.addMapItemView(this)
|
||||
anchors.fill: parent
|
||||
model: LiveLocationsModel {
|
||||
id: liveLocationsModel
|
||||
room: root.room
|
||||
@@ -49,7 +54,7 @@ Kirigami.Page {
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
text: i18n("There are no locations shared in this room.")
|
||||
visible: mapView.mapItems.length === 0
|
||||
visible: mapView.map.mapItems.length === 0
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
Connections {
|
||||
|
||||
@@ -7,11 +7,14 @@ import QtQuick
|
||||
import QtLocation
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property string userAgent: Application.name + "/" + Application.version + " (kde-devel@kde.org)"
|
||||
property var plugin: Plugin {
|
||||
name: "osm"
|
||||
PluginParameter {
|
||||
name: "osm.useragent"
|
||||
value: Application.name + "/" + Application.version + " (kde-devel@kde.org)"
|
||||
value: root.userAgent
|
||||
}
|
||||
PluginParameter {
|
||||
name: "osm.mapping.providersrepository.address"
|
||||
|
||||
@@ -38,7 +38,7 @@ Kirigami.Page {
|
||||
formats: Prison.Format.QRCode | Prison.Format.Aztec
|
||||
onResultChanged: {
|
||||
if (result.text.length > 0 && result.text != scanner.previousText) {
|
||||
RoomManager.resolveResource(result.text, "");
|
||||
RoomManager.resolveResource(result.text, "qr");
|
||||
scanner.previousText = result.text;
|
||||
}
|
||||
root.closeDialog();
|
||||
|
||||
@@ -66,6 +66,7 @@ QQC2.Dialog {
|
||||
root.close();
|
||||
}
|
||||
focusSequence: ""
|
||||
onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text
|
||||
}
|
||||
|
||||
QQC2.ScrollView {
|
||||
@@ -81,13 +82,7 @@ QQC2.Dialog {
|
||||
highlightMoveDuration: 200
|
||||
Keys.forwardTo: searchField
|
||||
keyNavigationEnabled: true
|
||||
model: SortFilterRoomListModel {
|
||||
filterText: searchField.text
|
||||
sourceModel: RoomListModel {
|
||||
id: roomListModel
|
||||
connection: root.connection
|
||||
}
|
||||
}
|
||||
model: RoomManager.sortFilterRoomListModel
|
||||
|
||||
delegate: RoomDelegate {
|
||||
connection: root.connection
|
||||
|
||||
@@ -115,6 +115,20 @@ QQC2.ScrollView {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: leaveButton
|
||||
icon.name: "arrow-left"
|
||||
text: i18nc("@action:button", "Leave this room")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
Qt.createComponent('org.kde.neochat', 'ConfirmLeaveDialog.qml').createObject(root.QQC2.ApplicationWindow.window, {
|
||||
room: root.room
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Members")
|
||||
activeFocusOnTab: false
|
||||
|
||||
@@ -29,16 +29,15 @@ Kirigami.Page {
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
readonly property RoomTreeModel roomTreeModel: RoomTreeModel {
|
||||
connection: root.connection
|
||||
}
|
||||
property bool spaceChanging: true
|
||||
|
||||
readonly property bool collapsed: Config.collapsed
|
||||
|
||||
onCurrentWidthChanged: pageStack.defaultColumnWidth = root.currentWidth
|
||||
Component.onCompleted: pageStack.defaultColumnWidth = root.currentWidth
|
||||
|
||||
|
||||
onCollapsedChanged: {
|
||||
if (collapsed) {
|
||||
sortFilterRoomTreeModel.filterText = "";
|
||||
RoomManager.sortFilterRoomTreeModel.filterText = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +86,13 @@ Kirigami.Page {
|
||||
|
||||
padding: 0
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onCurrentSpaceChanged() {
|
||||
treeView.expandRecursively();
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
@@ -97,9 +103,6 @@ Kirigami.Page {
|
||||
Layout.fillHeight: true
|
||||
|
||||
connection: root.connection
|
||||
|
||||
onSelectionChanged: root.spaceChanging = true
|
||||
onSpacesUpdated: sortFilterRoomTreeModel.invalidate()
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
@@ -127,33 +130,7 @@ Kirigami.Page {
|
||||
clip: true
|
||||
reuseItems: false
|
||||
|
||||
onLayoutChanged: {
|
||||
if (sortFilterRoomTreeModel.filterTextJustChanged) {
|
||||
treeView.expandRecursively();
|
||||
sortFilterRoomTreeModel.filterTextJustChanged = false;
|
||||
}
|
||||
if (root.spaceChanging) {
|
||||
treeView.expandRecursively();
|
||||
if (spaceDrawer.showDirectChats || spaceDrawer.selectedSpaceId.length < 1) {
|
||||
const item = treeView.itemAtIndex(treeView.index(1, 0))
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
RoomManager.resolveResource(item.currentRoom.id);
|
||||
}
|
||||
root.spaceChanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
model: SortFilterRoomTreeModel {
|
||||
id: sortFilterRoomTreeModel
|
||||
|
||||
property bool filterTextJustChanged: false
|
||||
|
||||
sourceModel: root.roomTreeModel
|
||||
activeSpaceId: spaceDrawer.selectedSpaceId
|
||||
mode: spaceDrawer.showDirectChats ? SortFilterRoomTreeModel.DirectChats : SortFilterRoomTreeModel.Rooms
|
||||
}
|
||||
model: RoomManager.sortFilterRoomTreeModel
|
||||
|
||||
selectionModel: ItemSelectionModel {}
|
||||
|
||||
@@ -226,7 +203,7 @@ Kirigami.Page {
|
||||
anchors.horizontalCenterOffset: (spaceDrawer.width + 1) / 2
|
||||
width: scrollView.width - Kirigami.Units.largeSpacing * 4
|
||||
visible: treeView.rows == 0
|
||||
text: if (sortFilterRoomTreeModel.filterText.length > 0) {
|
||||
text: if (RoomManager.sortFilterRoomTreeModel.filterText.length > 0) {
|
||||
return spaceDrawer.showDirectChats ? i18n("No friends found") : i18n("No rooms found");
|
||||
} else {
|
||||
return spaceDrawer.showDirectChats ? i18n("You haven't added any of your friends yet, click below to search for them.") : i18n("Join some rooms to get started");
|
||||
@@ -235,12 +212,12 @@ Kirigami.Page {
|
||||
|
||||
Kirigami.Action {
|
||||
id: exploreRoomAction
|
||||
icon.name: sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
|
||||
text: sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms")
|
||||
icon.name: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
|
||||
text: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms")
|
||||
onTriggered: {
|
||||
let dialog = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage.qml'), {
|
||||
connection: root.connection,
|
||||
keyword: sortFilterRoomTreeModel.filterText
|
||||
keyword: RoomManager.sortFilterRoomTreeModel.filterText
|
||||
}, {
|
||||
title: i18nc("@title", "Explore Rooms")
|
||||
});
|
||||
@@ -252,8 +229,8 @@ Kirigami.Page {
|
||||
|
||||
Kirigami.Action {
|
||||
id: userSearchAction
|
||||
icon.name: sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
|
||||
text: sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in friend directory") : i18n("Find your friends")
|
||||
icon.name: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
|
||||
text: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in friend directory") : i18n("Find your friends")
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
@@ -334,8 +311,8 @@ Kirigami.Page {
|
||||
connection: root.connection
|
||||
|
||||
onTextChanged: newText => {
|
||||
sortFilterRoomTreeModel.filterText = newText;
|
||||
sortFilterRoomTreeModel.filterTextJustChanged = true;
|
||||
RoomManager.sortFilterRoomTreeModel.filterText = newText;
|
||||
treeView.expandRecursively();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,7 +323,7 @@ Kirigami.Page {
|
||||
connection: root.connection
|
||||
|
||||
onTextChanged: newText => {
|
||||
sortFilterRoomTreeModel.filterText = newText;
|
||||
RoomManager.sortFilterRoomTreeModel.filterText = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ Kirigami.Page {
|
||||
/// Disable cancel shortcut. Used by the separate window since it provides its own cancel implementation.
|
||||
property bool disableCancelShortcut: false
|
||||
|
||||
title: root.currentRoom.displayName
|
||||
title: root.currentRoom ? root.currentRoom.displayName : ""
|
||||
focus: true
|
||||
padding: 0
|
||||
|
||||
@@ -116,10 +116,11 @@ Kirigami.Page {
|
||||
Loader {
|
||||
id: timelineViewLoader
|
||||
anchors.fill: parent
|
||||
active: root.currentRoom && !root.currentRoom.isInvite && !root.loading
|
||||
active: root.currentRoom && !root.currentRoom.isInvite && !root.loading && !root.currentRoom.isSpace
|
||||
sourceComponent: TimelineView {
|
||||
id: timelineView
|
||||
currentRoom: root.currentRoom
|
||||
page: root
|
||||
timelineModel: root.timelineModel
|
||||
messageFilterModel: root.messageFilterModel
|
||||
actionsHandler: root.actionsHandler
|
||||
@@ -142,7 +143,23 @@ Kirigami.Page {
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.loading && !invitationLoader.active
|
||||
id: spaceLoader
|
||||
active: root.currentRoom && root.currentRoom.isSpace
|
||||
anchors.fill: parent
|
||||
sourceComponent: SpaceHomePage {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !RoomManager.currentRoom
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
icon.name: "org.kde.neochat"
|
||||
text: i18n("Welcome to NeoChat")
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.loading && !invitationLoader.active && RoomManager.currentRoom && !spaceLoader.active
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: Kirigami.LoadingPlaceholder {
|
||||
anchors.centerIn: parent
|
||||
@@ -175,11 +192,7 @@ Kirigami.Page {
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onCurrentRoomChanged() {
|
||||
if (!RoomManager.currentRoom) {
|
||||
if (pageStack.lastItem === root) {
|
||||
pageStack.pop();
|
||||
}
|
||||
} else if (root.currentRoom.isInvite) {
|
||||
if (root.currentRoom && root.currentRoom.isInvite) {
|
||||
root.currentRoom.clearInvitationNotification();
|
||||
}
|
||||
}
|
||||
@@ -187,6 +200,10 @@ Kirigami.Page {
|
||||
function onWarning(title, message) {
|
||||
root.warning(title, message);
|
||||
}
|
||||
|
||||
function onGoToEvent(eventId) {
|
||||
(timelineViewLoader.item as TimelineView).goToEvent(eventId);
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
@@ -281,6 +298,15 @@ Kirigami.Page {
|
||||
});
|
||||
popup.open();
|
||||
}
|
||||
|
||||
function onShowMaximizedCode(author, time, codeText, language) {
|
||||
let popup = Qt.createComponent('org.kde.neochat', 'CodeMaximizeComponent.qml').createObject(QQC2.Overlay.overlay, {
|
||||
author: author,
|
||||
time: time,
|
||||
codeText: codeText,
|
||||
language: language
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
|
||||
@@ -82,6 +82,13 @@ Kirigami.ScrollablePage {
|
||||
searchField.forceActiveFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Force the search to be updated if the model has a valid search function.
|
||||
*/
|
||||
function updateSearch() {
|
||||
searchTimer.restart();
|
||||
}
|
||||
|
||||
header: QQC2.Control {
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
|
||||
@@ -119,11 +126,18 @@ Kirigami.ScrollablePage {
|
||||
QQC2.Button {
|
||||
id: searchButton
|
||||
icon.name: "search"
|
||||
display: QQC2.Button.IconOnly
|
||||
text: i18nc("@action:button", "Search")
|
||||
|
||||
onClicked: {
|
||||
if (typeof model.search === 'function') {
|
||||
model.search();
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
Timer {
|
||||
id: searchTimer
|
||||
|
||||
@@ -51,7 +51,8 @@ Kirigami.Dialog {
|
||||
onClicked: {
|
||||
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage.qml'), {
|
||||
connection: root.room.connection,
|
||||
showOnlySpaces: true
|
||||
showOnlySpaces: true,
|
||||
showOnlySpacesButton: false
|
||||
}, {
|
||||
title: i18nc("@title", "Choose Parent Space")
|
||||
});
|
||||
@@ -135,7 +136,8 @@ Kirigami.Dialog {
|
||||
onClicked: {
|
||||
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage.qml'), {
|
||||
connection: root.room.connection,
|
||||
showOnlySpaces: true
|
||||
showOnlySpaces: true,
|
||||
showOnlySpacesButton: false
|
||||
}, {
|
||||
title: i18nc("@title", "Explore Rooms")
|
||||
});
|
||||
|
||||
@@ -21,20 +21,6 @@ QQC2.Control {
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
property string selectedSpaceId: RoomManager.lastSpaceId
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onConnectionChanged() {
|
||||
// We need to rebind as any previous change will have been overwritten.
|
||||
selectedSpaceId = RoomManager.lastSpaceId;
|
||||
}
|
||||
}
|
||||
|
||||
property bool showDirectChats: RoomManager.directChatsActive
|
||||
|
||||
signal selectionChanged
|
||||
signal spacesUpdated
|
||||
|
||||
contentItem: Loader {
|
||||
id: sidebarColumn
|
||||
z: 0
|
||||
@@ -100,12 +86,9 @@ QQC2.Control {
|
||||
source: "user-home-symbolic"
|
||||
}
|
||||
|
||||
checked: root.selectedSpaceId === "" && root.showDirectChats === false
|
||||
checked: RoomManager.currentSpace.length === 0
|
||||
onClicked: {
|
||||
root.showDirectChats = false;
|
||||
RoomManager.directChatsActive = false;
|
||||
root.selectedSpaceId = "";
|
||||
RoomManager.lastSpaceId = "";
|
||||
RoomManager.currentSpace = "";
|
||||
root.selectionChanged();
|
||||
}
|
||||
|
||||
@@ -119,7 +102,7 @@ QQC2.Control {
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: root.connection.homeNotifications > 0 ? root.connection.homeNotifications : ""
|
||||
visible: root.connection.homeNotifications > 0 && (root.selectedSpaceId !== "" || root.showDirectChats === true)
|
||||
visible: root.connection.homeNotifications > 0 && (RoomManager.currentSpace.length > 0 || root.showDirectChats === true)
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
@@ -149,12 +132,9 @@ QQC2.Control {
|
||||
source: "system-users"
|
||||
}
|
||||
|
||||
checked: root.showDirectChats === true
|
||||
checked: RoomManager.currentSpace === "DM"
|
||||
onClicked: {
|
||||
root.showDirectChats = true;
|
||||
RoomManager.directChatsActive = true;
|
||||
root.selectedSpaceId = "";
|
||||
RoomManager.lastSpaceId = "";
|
||||
RoomManager.currentSpace = "DM";
|
||||
root.selectionChanged();
|
||||
}
|
||||
|
||||
@@ -187,17 +167,7 @@ QQC2.Control {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: SortFilterSpaceListModel {
|
||||
sourceModel: RoomListModel {
|
||||
connection: root.connection
|
||||
}
|
||||
onLayoutChanged: root.spacesUpdated()
|
||||
}
|
||||
onCountChanged: {
|
||||
if (!root.connection.room(root.selectedSpaceId)) {
|
||||
root.selectedSpaceId = "";
|
||||
}
|
||||
}
|
||||
model: RoomManager.sortFilterSpaceListModel
|
||||
|
||||
delegate: AvatarTabButton {
|
||||
id: spaceDelegate
|
||||
@@ -215,17 +185,10 @@ QQC2.Control {
|
||||
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||
|
||||
onSelected: {
|
||||
root.showDirectChats = false;
|
||||
RoomManager.directChatsActive = false;
|
||||
if (!SpaceHierarchyCache.isSpaceChild(roomId, RoomManager.currentRoom.id) || root.selectedSpaceId == roomId) {
|
||||
RoomManager.resolveResource(currentRoom.id);
|
||||
} else {
|
||||
RoomManager.lastSpaceId = currentRoom.id;
|
||||
}
|
||||
root.selectedSpaceId = roomId;
|
||||
root.selectionChanged();
|
||||
RoomManager.resolveResource(spaceDelegate.roomId);
|
||||
RoomManager.currentSpace = spaceDelegate.roomId;
|
||||
}
|
||||
checked: root.selectedSpaceId === roomId
|
||||
checked: RoomManager.currentSpace === roomId
|
||||
onContextMenuRequested: root.createContextMenu(currentRoom)
|
||||
|
||||
QQC2.Label {
|
||||
@@ -238,7 +201,7 @@ QQC2.Control {
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: spaceDelegate.currentRoom.childrenNotificationCount > 0 ? spaceDelegate.currentRoom.childrenNotificationCount : ""
|
||||
visible: spaceDelegate.currentRoom.childrenNotificationCount > 0 && root.selectedSpaceId != spaceDelegate.roomId
|
||||
visible: spaceDelegate.currentRoom.childrenNotificationCount > 0 && RoomManager.currentSpace != spaceDelegate.roomId
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
@@ -10,152 +10,153 @@ import org.kde.kirigami as Kirigami
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
|
||||
Kirigami.Page {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
readonly property NeoChatRoom currentRoom: RoomManager.currentRoom
|
||||
|
||||
padding: 0
|
||||
anchors.fill: parent
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: headerItem
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
implicitHeight: headerColumn.implicitHeight
|
||||
QQC2.Control {
|
||||
id: headerItem
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
implicitHeight: headerColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: headerColumn
|
||||
anchors.centerIn: headerItem
|
||||
width: sizeHelper.currentWidth
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
}
|
||||
|
||||
GroupChatDrawerHeader {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
room: root.currentRoom
|
||||
ColumnLayout {
|
||||
id: headerColumn
|
||||
anchors.centerIn: headerItem
|
||||
width: sizeHelper.currentWidth
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
GroupChatDrawerHeader {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
room: root.currentRoom
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
QQC2.Button {
|
||||
visible: root.currentRoom.canSendState("invite")
|
||||
text: i18nc("@button", "Invite user to space")
|
||||
icon.name: "list-add-user"
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'InviteUserPage.qml'), {
|
||||
room: root.currentRoom
|
||||
}, {
|
||||
title: i18nc("@title", "Invite a User")
|
||||
})
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
QQC2.Button {
|
||||
visible: root.currentRoom.canSendState("invite")
|
||||
text: i18nc("@button", "Invite user to space")
|
||||
icon.name: "list-add-user"
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'InviteUserPage.qml'), {
|
||||
room: root.currentRoom
|
||||
}, {
|
||||
title: i18nc("@title", "Invite a User")
|
||||
})
|
||||
}
|
||||
QQC2.Button {
|
||||
visible: root.currentRoom.canSendState("m.space.child")
|
||||
text: i18nc("@button", "Add new room")
|
||||
icon.name: "list-add"
|
||||
onClicked: _private.createRoom(root.currentRoom.id)
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@button", "Leave the space")
|
||||
icon.name: "go-previous"
|
||||
onClicked: RoomManager.leaveRoom(root.currentRoom)
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@button", "Space settings")
|
||||
icon.name: "settings-configure"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: root.currentRoom,
|
||||
connection: root.currentRoom.connection
|
||||
}, {
|
||||
title: i18n("Room Settings")
|
||||
})
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
QQC2.Button {
|
||||
visible: root.currentRoom.canSendState("m.space.child")
|
||||
text: i18nc("@button", "Add new room")
|
||||
icon.name: "list-add"
|
||||
onClicked: _private.createRoom(root.currentRoom.id)
|
||||
}
|
||||
Kirigami.SearchField {
|
||||
QQC2.Button {
|
||||
text: i18nc("@button", "Leave the space")
|
||||
icon.name: "go-previous"
|
||||
onClicked: RoomManager.leaveRoom(root.currentRoom)
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
onTextChanged: spaceChildSortFilterModel.filterText = text
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@button", "Space settings")
|
||||
icon.name: "settings-configure"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: root.currentRoom,
|
||||
connection: root.currentRoom.connection
|
||||
}, {
|
||||
title: i18n("Room Settings")
|
||||
})
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
}
|
||||
DelegateSizeHelper {
|
||||
id: sizeHelper
|
||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
||||
startPercentWidth: 100
|
||||
endPercentWidth: 85
|
||||
maxWidth: Kirigami.Units.gridUnit * 60
|
||||
|
||||
parentWidth: columnLayout.width
|
||||
Kirigami.SearchField {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
onTextChanged: spaceChildSortFilterModel.filterText = text
|
||||
}
|
||||
}
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.ScrollView {
|
||||
id: hierarchyScrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: !spaceChildrenModel.loading
|
||||
DelegateSizeHelper {
|
||||
id: sizeHelper
|
||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
||||
startPercentWidth: 100
|
||||
endPercentWidth: 85
|
||||
maxWidth: Kirigami.Units.gridUnit * 60
|
||||
|
||||
TreeView {
|
||||
id: spaceTree
|
||||
columnWidthProvider: function (column) {
|
||||
return spaceTree.width;
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
model: SpaceChildSortFilterModel {
|
||||
id: spaceChildSortFilterModel
|
||||
sourceModel: SpaceChildrenModel {
|
||||
id: spaceChildrenModel
|
||||
space: root.currentRoom
|
||||
}
|
||||
}
|
||||
|
||||
delegate: SpaceHierarchyDelegate {
|
||||
onCreateRoom: _private.createRoom(roomId)
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: spaceChildrenModel.loading
|
||||
|
||||
Loader {
|
||||
active: spaceChildrenModel.loading
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: Kirigami.LoadingPlaceholder {}
|
||||
}
|
||||
parentWidth: root.width
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.ScrollView {
|
||||
id: hierarchyScrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: !spaceChildrenModel.loading
|
||||
|
||||
TreeView {
|
||||
id: spaceTree
|
||||
columnWidthProvider: function (column) {
|
||||
return spaceTree.width;
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
model: SpaceChildSortFilterModel {
|
||||
id: spaceChildSortFilterModel
|
||||
sourceModel: SpaceChildrenModel {
|
||||
id: spaceChildrenModel
|
||||
space: root.currentRoom
|
||||
}
|
||||
}
|
||||
|
||||
delegate: SpaceHierarchyDelegate {
|
||||
onCreateRoom: _private.createRoom(roomId)
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
}
|
||||
}
|
||||
QQC2.Control {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: spaceChildrenModel.loading
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: spaceChildrenModel.loading
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: Kirigami.LoadingPlaceholder {}
|
||||
}
|
||||
}
|
||||
QtObject {
|
||||
id: _private
|
||||
function createRoom(parentId) {
|
||||
@@ -182,3 +183,5 @@ Kirigami.Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ QQC2.ScrollView {
|
||||
}
|
||||
property bool roomChanging: false
|
||||
|
||||
required property Item page
|
||||
|
||||
/**
|
||||
* @brief The TimelineModel to use.
|
||||
*
|
||||
@@ -299,14 +301,14 @@ QQC2.ScrollView {
|
||||
|
||||
Timer {
|
||||
id: markReadIfVisibleTimer
|
||||
running: messageListView.allUnreadVisible() && applicationWindow().active && (root.currentRoom.timelineSize > 0 || root.currentRoom.allHistoryLoaded)
|
||||
running: messageListView.allUnreadVisible() && applicationWindow().active && (root.currentRoom.timelineSize > 0 || root.currentRoom.allHistoryLoaded) && applicationWindow().pageStack.visibleItems.includes(root.page)
|
||||
interval: 10000
|
||||
onTriggered: root.currentRoom.markAllMessagesAsRead()
|
||||
|
||||
function reset() {
|
||||
restart();
|
||||
running = Qt.binding(function () {
|
||||
return messageListView.allUnreadVisible() && applicationWindow().active && (root.currentRoom.timelineSize > 0 || root.currentRoom.allHistoryLoaded);
|
||||
return messageListView.allUnreadVisible() && applicationWindow().active && (root.currentRoom.timelineSize > 0 || root.currentRoom.allHistoryLoaded) && applicationWindow().pageStack.visibleItems.includes(root.page);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -420,4 +422,8 @@ QQC2.ScrollView {
|
||||
function positionViewAtBeginning() {
|
||||
messageListView.positionViewAtBeginning();
|
||||
}
|
||||
|
||||
function goToEvent(eventId) {
|
||||
messageListView.goToEvent(eventId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,34 +15,20 @@ import org.kde.neochat.accounts
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
|
||||
property int columnWidth: Kirigami.Units.gridUnit * 13
|
||||
|
||||
property RoomListPage roomListPage
|
||||
property bool roomListLoaded: false
|
||||
|
||||
property RoomPage roomPage
|
||||
property SpaceHomePage spaceHomePage
|
||||
|
||||
property NeoChatConnection connection: Controller.activeConnection
|
||||
|
||||
minimumWidth: Kirigami.Units.gridUnit * 20
|
||||
minimumHeight: Kirigami.Units.gridUnit * 15
|
||||
|
||||
visible: false // Will be overridden in Component.onCompleted
|
||||
wideScreen: width > columnWidth * 5
|
||||
wideScreen: width > Kirigami.Units.gridUnit * 65
|
||||
|
||||
pageStack {
|
||||
initialPage: WelcomePage {
|
||||
showExisting: true
|
||||
onConnectionChosen: {
|
||||
pageStack.replace(roomListComponent);
|
||||
roomListLoaded = true;
|
||||
roomListPage = pageStack.currentItem;
|
||||
RoomManager.loadInitialRoom();
|
||||
}
|
||||
onConnectionChosen: root.load()
|
||||
}
|
||||
globalToolBar.canContainHandles: true
|
||||
defaultColumnWidth: roomListPage ? roomListPage.currentWidth : 0
|
||||
globalToolBar {
|
||||
style: Kirigami.ApplicationHeaderStyle.ToolBar
|
||||
showNavigationButtons: pageStack.currentIndex > 0 || pageStack.layers.depth > 1 ? Kirigami.ApplicationHeaderStyle.ShowBackButton : 0
|
||||
@@ -52,7 +38,6 @@ Kirigami.ApplicationWindow {
|
||||
onConnectionChanged: {
|
||||
CustomEmojiModel.connection = root.connection;
|
||||
MatrixImageProvider.connection = root.connection;
|
||||
RoomManager.connection = root.connection;
|
||||
SpaceHierarchyCache.connection = root.connection;
|
||||
if (ShareHandler.text && root.connection) {
|
||||
root.handleShare();
|
||||
@@ -62,10 +47,7 @@ Kirigami.ApplicationWindow {
|
||||
Connections {
|
||||
target: LoginHelper
|
||||
function onLoaded() {
|
||||
pageStack.replace(roomListComponent);
|
||||
roomListLoaded = true;
|
||||
roomListPage = pageStack.currentItem;
|
||||
RoomManager.loadInitialRoom();
|
||||
root.load();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,16 +108,6 @@ Kirigami.ApplicationWindow {
|
||||
Connections {
|
||||
target: RoomManager
|
||||
|
||||
function onPushRoom(room, event) {
|
||||
root.roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage.qml'), {
|
||||
connection: root.connection
|
||||
});
|
||||
root.roomPage.forceActiveFocus();
|
||||
if (event.length > 0) {
|
||||
roomPage.goToEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function onAskJoinRoom(room) {
|
||||
joinRoomDialog.createObject(applicationWindow(), {
|
||||
room: room,
|
||||
@@ -147,38 +119,6 @@ Kirigami.ApplicationWindow {
|
||||
root.showUserDetail(user);
|
||||
}
|
||||
|
||||
function onPushSpaceHome(room) {
|
||||
root.spaceHomePage = pageStack.push(Qt.createComponent('org.kde.neochat', 'SpaceHomePage.qml'));
|
||||
root.spaceHomePage.forceActiveFocus();
|
||||
}
|
||||
|
||||
function onReplaceRoom(room, event) {
|
||||
if (root.roomPage) {
|
||||
pageStack.currentIndex = pageStack.depth - 1;
|
||||
} else {
|
||||
pageStack.pop();
|
||||
root.roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage.qml'), {
|
||||
connection: root.connection
|
||||
});
|
||||
root.spaceHomePage = null;
|
||||
}
|
||||
root.roomPage.forceActiveFocus();
|
||||
if (event.length > 0) {
|
||||
root.roomPage.goToEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function onReplaceSpaceHome(room) {
|
||||
if (root.spaceHomePage) {
|
||||
pageStack.currentIndex = pageStack.depth - 1;
|
||||
} else {
|
||||
pageStack.pop();
|
||||
root.spaceHomePage = pageStack.push(Qt.createComponent('org.kde.neochat', 'SpaceHomePage.qml'));
|
||||
root.roomPage = null;
|
||||
}
|
||||
root.spaceHomePage.forceActiveFocus();
|
||||
}
|
||||
|
||||
function goToEvent(event) {
|
||||
if (event.length > 0) {
|
||||
roomItem.goToEvent(event);
|
||||
@@ -247,7 +187,6 @@ Kirigami.ApplicationWindow {
|
||||
Component.onCompleted: {
|
||||
CustomEmojiModel.connection = root.connection;
|
||||
MatrixImageProvider.connection = root.connection;
|
||||
RoomManager.connection = root.connection;
|
||||
SpaceHierarchyCache.connection = root.connection;
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
if (ShareHandler.text && root.connection) {
|
||||
@@ -319,9 +258,7 @@ Kirigami.ApplicationWindow {
|
||||
target: AccountRegistry
|
||||
function onRowsRemoved() {
|
||||
if (AccountRegistry.rowCount() === 0) {
|
||||
RoomManager.reset();
|
||||
pageStack.clear();
|
||||
roomListLoaded = false;
|
||||
pageStack.push(Qt.createComponent('org.kde.neochat', '.qml'));
|
||||
}
|
||||
}
|
||||
@@ -476,7 +413,7 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
function handleShare(): void {
|
||||
const dialog = applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ChooseRoomDialog.qml", {
|
||||
const dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Share"),
|
||||
@@ -496,6 +433,15 @@ Kirigami.ApplicationWindow {
|
||||
}).open();
|
||||
}
|
||||
|
||||
function load() {
|
||||
pageStack.replace(roomListComponent);
|
||||
RoomManager.loadInitialRoom();
|
||||
let roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage.qml'), {
|
||||
connection: root.connection
|
||||
});
|
||||
roomPage.forceActiveFocus();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userDetailDialog
|
||||
UserDetailDialog {}
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
#include "roommanager.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "controller.h"
|
||||
#include "eventhandler.h"
|
||||
#include "messagecomponenttype.h"
|
||||
#include "models/timelinemodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "spacehierarchycache.h"
|
||||
#include "urlhelper.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDesktopServices>
|
||||
@@ -28,9 +29,12 @@
|
||||
|
||||
RoomManager::RoomManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_currentRoom(nullptr)
|
||||
, m_lastCurrentRoom(nullptr)
|
||||
, m_config(KSharedConfig::openStateConfig())
|
||||
, m_roomListModel(new RoomListModel(this))
|
||||
, m_sortFilterRoomListModel(new SortFilterRoomListModel(m_roomListModel, this))
|
||||
, m_sortFilterSpaceListModel(new SortFilterSpaceListModel(m_roomListModel, this))
|
||||
, m_roomTreeModel(new RoomTreeModel(this))
|
||||
, m_sortFilterRoomTreeModel(new SortFilterRoomTreeModel(m_roomTreeModel, this))
|
||||
, m_timelineModel(new TimelineModel(this))
|
||||
, m_messageFilterModel(new MessageFilterModel(this, m_timelineModel))
|
||||
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
||||
@@ -42,6 +46,15 @@ RoomManager::RoomManager(QObject *parent)
|
||||
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
||||
m_timelineModel->setRoom(m_currentRoom);
|
||||
});
|
||||
|
||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this](NeoChatConnection *connection) {
|
||||
setConnection(connection);
|
||||
});
|
||||
connect(this, &RoomManager::connectionChanged, this, [this]() {
|
||||
m_roomListModel->setConnection(m_connection);
|
||||
m_roomTreeModel->setConnection(m_connection);
|
||||
});
|
||||
connect(m_sortFilterSpaceListModel, &SortFilterSpaceListModel::layoutChanged, m_sortFilterRoomTreeModel, &SortFilterRoomTreeModel::invalidate);
|
||||
}
|
||||
|
||||
RoomManager::~RoomManager()
|
||||
@@ -59,6 +72,31 @@ NeoChatRoom *RoomManager::currentRoom() const
|
||||
return m_currentRoom;
|
||||
}
|
||||
|
||||
RoomListModel *RoomManager::roomListModel() const
|
||||
{
|
||||
return m_roomListModel;
|
||||
}
|
||||
|
||||
SortFilterRoomListModel *RoomManager::sortFilterRoomListModel() const
|
||||
{
|
||||
return m_sortFilterRoomListModel;
|
||||
}
|
||||
|
||||
SortFilterSpaceListModel *RoomManager::sortFilterSpaceListModel() const
|
||||
{
|
||||
return m_sortFilterSpaceListModel;
|
||||
}
|
||||
|
||||
RoomTreeModel *RoomManager::roomTreeModel() const
|
||||
{
|
||||
return m_roomTreeModel;
|
||||
}
|
||||
|
||||
SortFilterRoomTreeModel *RoomManager::sortFilterRoomTreeModel() const
|
||||
{
|
||||
return m_sortFilterRoomTreeModel;
|
||||
}
|
||||
|
||||
TimelineModel *RoomManager::timelineModel() const
|
||||
{
|
||||
return m_timelineModel;
|
||||
@@ -87,6 +125,11 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
|
||||
return;
|
||||
}
|
||||
|
||||
if (uri.type() == Uri::NonMatrix && action == "qr"_ls) {
|
||||
Q_EMIT externalUrl(uri.toUrl());
|
||||
return;
|
||||
}
|
||||
|
||||
if (uri.type() != Uri::NonMatrix) {
|
||||
if (!m_connection) {
|
||||
return;
|
||||
@@ -99,7 +142,7 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
|
||||
|
||||
const auto result = visitResource(m_connection, uri);
|
||||
if (result == Quotient::CouldNotResolve) {
|
||||
if (uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) {
|
||||
if ((uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) && action != "no_join"_ls) {
|
||||
Q_EMIT askJoinRoom(uri.primaryId());
|
||||
}
|
||||
} else { // Invalid cases should have been eliminated earlier
|
||||
@@ -121,6 +164,14 @@ void RoomManager::maximizeMedia(int index)
|
||||
Q_EMIT showMaximizedMedia(index);
|
||||
}
|
||||
|
||||
void RoomManager::maximizeCode(const QVariantMap &author, const QDateTime &time, const QString &codeText, const QString &language)
|
||||
{
|
||||
if (codeText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Q_EMIT showMaximizedCode(author, time, codeText, language);
|
||||
}
|
||||
|
||||
void RoomManager::requestFullScreenClose()
|
||||
{
|
||||
Q_EMIT closeFullScreen();
|
||||
@@ -182,72 +233,17 @@ void RoomManager::loadInitialRoom()
|
||||
connect(this, &RoomManager::connectionChanged, this, &RoomManager::openRoomForActiveConnection);
|
||||
}
|
||||
|
||||
QString RoomManager::lastSpaceId() const
|
||||
{
|
||||
if (!m_connection) {
|
||||
return {};
|
||||
}
|
||||
return m_lastSpaceConfig.readEntry(m_connection->userId(), QString());
|
||||
}
|
||||
|
||||
void RoomManager::setLastSpaceId(const QString &lastSpaceId)
|
||||
{
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentLastSpaceId = m_lastSpaceConfig.readEntry(m_connection->userId(), QString());
|
||||
if (lastSpaceId == currentLastSpaceId) {
|
||||
return;
|
||||
}
|
||||
m_lastSpaceConfig.writeEntry(m_connection->userId(), lastSpaceId);
|
||||
Q_EMIT lastSpaceIdChanged();
|
||||
}
|
||||
|
||||
bool RoomManager::directChatsActive() const
|
||||
{
|
||||
if (!m_connection) {
|
||||
return {};
|
||||
}
|
||||
return m_directChatsConfig.readEntry(m_connection->userId(), bool());
|
||||
}
|
||||
|
||||
void RoomManager::setDirectChatsActive(bool directChatsActive)
|
||||
{
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentDirectChatsActive = m_directChatsConfig.readEntry(m_connection->userId(), bool());
|
||||
if (directChatsActive == currentDirectChatsActive) {
|
||||
return;
|
||||
}
|
||||
m_directChatsConfig.writeEntry(m_connection->userId(), directChatsActive);
|
||||
Q_EMIT directChatsActiveChanged();
|
||||
}
|
||||
|
||||
void RoomManager::openRoomForActiveConnection()
|
||||
{
|
||||
if (!m_connection) {
|
||||
return;
|
||||
m_currentRoom = nullptr;
|
||||
}
|
||||
// Read from last open room
|
||||
QString roomId = m_lastRoomConfig.readEntry(m_connection->userId(), QString());
|
||||
|
||||
// TODO remove legacy check at some point.
|
||||
if (roomId.isEmpty()) {
|
||||
roomId = NeoChatConfig::self()->openRoom();
|
||||
}
|
||||
|
||||
if (!roomId.isEmpty()) {
|
||||
// Here we can cast because the controller has been configured to
|
||||
// return NeoChatRoom instead of simple Quotient::Room
|
||||
const auto room = qobject_cast<NeoChatRoom *>(m_connection->room(roomId));
|
||||
|
||||
if (room) {
|
||||
resolveResource(room->id());
|
||||
}
|
||||
if (m_lastRoomConfig.readEntry(m_connection->userId(), QString()).isEmpty()) {
|
||||
setCurrentRoom({});
|
||||
} else {
|
||||
resolveResource(m_lastRoomConfig.readEntry(m_connection->userId(), QString()));
|
||||
}
|
||||
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
|
||||
}
|
||||
|
||||
UriResolveResult RoomManager::visitUser(User *user, const QString &action)
|
||||
@@ -268,10 +264,9 @@ UriResolveResult RoomManager::visitUser(User *user, const QString &action)
|
||||
return Quotient::UriResolved;
|
||||
}
|
||||
|
||||
void RoomManager::visitRoom(Room *room, const QString &eventId)
|
||||
void RoomManager::visitRoom(Room *r, const QString &eventId)
|
||||
{
|
||||
auto neoChatRoom = qobject_cast<NeoChatRoom *>(room);
|
||||
Q_ASSERT(neoChatRoom != nullptr);
|
||||
auto room = qobject_cast<NeoChatRoom *>(r);
|
||||
|
||||
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
|
||||
m_currentRoom->editCache()->setEditId({});
|
||||
@@ -280,41 +275,26 @@ void RoomManager::visitRoom(Room *room, const QString &eventId)
|
||||
// We're doing these things here because it is critical that they are switched at the same time
|
||||
if (m_chatDocumentHandler->document()) {
|
||||
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
m_chatDocumentHandler->setRoom(neoChatRoom);
|
||||
m_chatDocumentHandler->document()->textDocument()->setPlainText(neoChatRoom->mainCache()->savedText());
|
||||
neoChatRoom->mainCache()->setText(neoChatRoom->mainCache()->savedText());
|
||||
} else {
|
||||
m_chatDocumentHandler->setRoom(neoChatRoom);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_currentRoom) {
|
||||
if (m_currentRoom->id() == room->id()) {
|
||||
Q_EMIT goToEvent(eventId);
|
||||
} else {
|
||||
m_lastCurrentRoom = std::exchange(m_currentRoom, neoChatRoom);
|
||||
Q_EMIT currentRoomChanged();
|
||||
|
||||
if (neoChatRoom->isSpace()) {
|
||||
m_lastSpaceConfig.writeEntry(m_connection->userId(), room->id());
|
||||
Q_EMIT replaceSpaceHome(neoChatRoom);
|
||||
} else {
|
||||
Q_EMIT replaceRoom(neoChatRoom, eventId);
|
||||
m_chatDocumentHandler->setRoom(room);
|
||||
if (room) {
|
||||
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->mainCache()->savedText());
|
||||
room->mainCache()->setText(room->mainCache()->savedText());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_lastCurrentRoom = std::exchange(m_currentRoom, neoChatRoom);
|
||||
Q_EMIT currentRoomChanged();
|
||||
if (neoChatRoom->isSpace()) {
|
||||
m_lastSpaceConfig.writeEntry(m_connection->userId(), room->id());
|
||||
Q_EMIT pushSpaceHome(neoChatRoom);
|
||||
} else {
|
||||
Q_EMIT pushRoom(neoChatRoom, eventId);
|
||||
m_chatDocumentHandler->setRoom(room);
|
||||
}
|
||||
}
|
||||
|
||||
// Save last open room
|
||||
m_lastRoomConfig.writeEntry(m_connection->userId(), room->id());
|
||||
if (!room) {
|
||||
setCurrentRoom({});
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currentRoom && m_currentRoom->id() == room->id()) {
|
||||
Q_EMIT goToEvent(eventId);
|
||||
} else {
|
||||
setCurrentRoom(room->id());
|
||||
}
|
||||
}
|
||||
|
||||
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
|
||||
@@ -351,35 +331,22 @@ void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAl
|
||||
|
||||
bool RoomManager::visitNonMatrix(const QUrl &url)
|
||||
{
|
||||
Q_EMIT externalUrl(url);
|
||||
UrlHelper().openUrl(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RoomManager::reset()
|
||||
{
|
||||
m_arg = QString();
|
||||
m_currentRoom = nullptr;
|
||||
m_lastCurrentRoom = nullptr;
|
||||
Q_EMIT currentRoomChanged();
|
||||
}
|
||||
|
||||
void RoomManager::leaveRoom(NeoChatRoom *room)
|
||||
{
|
||||
if (!room) {
|
||||
return;
|
||||
}
|
||||
if (m_lastCurrentRoom && room->id() == m_lastCurrentRoom->id()) {
|
||||
m_lastCurrentRoom = nullptr;
|
||||
}
|
||||
|
||||
if (m_currentRoom && m_currentRoom->id() == room->id()) {
|
||||
m_currentRoom = m_lastCurrentRoom;
|
||||
m_lastCurrentRoom = nullptr;
|
||||
Q_EMIT currentRoomChanged();
|
||||
if (m_currentRoom->isSpace()) {
|
||||
Q_EMIT replaceSpaceHome(m_currentRoom);
|
||||
} else {
|
||||
Q_EMIT replaceRoom(m_currentRoom, {});
|
||||
}
|
||||
setCurrentRoom({});
|
||||
}
|
||||
|
||||
if (m_currentSpaceId == room->id()) {
|
||||
setCurrentSpace({});
|
||||
}
|
||||
|
||||
room->forget();
|
||||
@@ -397,11 +364,6 @@ void RoomManager::setChatDocumentHandler(ChatDocumentHandler *handler)
|
||||
Q_EMIT chatDocumentHandlerChanged();
|
||||
}
|
||||
|
||||
NeoChatConnection *RoomManager::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void RoomManager::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
@@ -411,4 +373,76 @@ void RoomManager::setConnection(NeoChatConnection *connection)
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
|
||||
{
|
||||
m_currentSpaceId = spaceId;
|
||||
|
||||
// This need to happen before the signal so TreeView.expandRecursively() can work nicely.
|
||||
m_sortFilterRoomTreeModel->setActiveSpaceId(m_currentSpaceId);
|
||||
m_sortFilterRoomTreeModel->setMode(m_currentSpaceId == QLatin1String("DM") ? SortFilterRoomTreeModel::DirectChats : SortFilterRoomTreeModel::Rooms);
|
||||
|
||||
Q_EMIT currentSpaceChanged();
|
||||
m_lastSpaceConfig.writeEntry(m_connection->userId(), spaceId);
|
||||
|
||||
if (!setRoom) {
|
||||
return;
|
||||
}
|
||||
if (spaceId.length() > 3) {
|
||||
resolveResource(spaceId, "no_join"_ls);
|
||||
} else {
|
||||
visitRoom({}, {});
|
||||
}
|
||||
}
|
||||
|
||||
void RoomManager::setCurrentRoom(const QString &roomId)
|
||||
{
|
||||
if (roomId.isEmpty()) {
|
||||
m_currentRoom = nullptr;
|
||||
} else {
|
||||
m_currentRoom = dynamic_cast<NeoChatRoom *>(m_connection->room(roomId));
|
||||
}
|
||||
Q_EMIT currentRoomChanged();
|
||||
if (m_connection) {
|
||||
m_lastRoomConfig.writeEntry(m_connection->userId(), roomId);
|
||||
}
|
||||
if (roomId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (m_currentRoom->isSpace()) {
|
||||
return;
|
||||
}
|
||||
if (m_currentRoom->isDirectChat()) {
|
||||
if (m_currentSpaceId != "DM"_ls) {
|
||||
setCurrentSpace("DM"_ls, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto &parentSpaces = SpaceHierarchyCache::instance().parentSpaces(roomId);
|
||||
if (parentSpaces.contains(m_currentSpaceId)) {
|
||||
return;
|
||||
}
|
||||
if (const auto &parent = m_connection->room(m_currentRoom->canonicalParent())) {
|
||||
for (const auto &parentParent : SpaceHierarchyCache::instance().parentSpaces(parent->id())) {
|
||||
if (SpaceHierarchyCache::instance().parentSpaces(parentParent).isEmpty()) {
|
||||
setCurrentSpace(parentParent, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setCurrentSpace(parent->id(), false);
|
||||
return;
|
||||
}
|
||||
for (const auto &space : parentSpaces) {
|
||||
if (SpaceHierarchyCache::instance().parentSpaces(space).isEmpty()) {
|
||||
setCurrentSpace(space, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setCurrentSpace({}, false);
|
||||
}
|
||||
|
||||
QString RoomManager::currentSpace() const
|
||||
{
|
||||
return m_currentSpaceId;
|
||||
}
|
||||
|
||||
#include "moc_roommanager.cpp"
|
||||
|
||||
@@ -9,24 +9,22 @@
|
||||
#include <QQmlEngine>
|
||||
#include <Quotient/room.h>
|
||||
#include <Quotient/uriresolver.h>
|
||||
#include <KConfigGroup>
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "models/mediamessagefiltermodel.h"
|
||||
#include "models/messagefiltermodel.h"
|
||||
#include "models/roomlistmodel.h"
|
||||
#include "models/roomtreemodel.h"
|
||||
#include "models/sortfilterroomlistmodel.h"
|
||||
#include "models/sortfilterroomtreemodel.h"
|
||||
#include "models/sortfilterspacelistmodel.h"
|
||||
#include "models/timelinemodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class NeoChatConnection;
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class Room;
|
||||
class User;
|
||||
}
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
/**
|
||||
@@ -45,8 +43,6 @@ class RoomManager : public QObject, public UriResolverBase
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The current open room in NeoChat, if any.
|
||||
*
|
||||
@@ -54,6 +50,47 @@ class RoomManager : public QObject, public UriResolverBase
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
||||
|
||||
/**
|
||||
* @brief The id of the space currently opened in the space drawer.
|
||||
*
|
||||
* If this is an empty string, the uncategorized rooms are shown.
|
||||
* If it is the string "DM", the DMs are shown.
|
||||
*/
|
||||
Q_PROPERTY(QString currentSpace READ currentSpace WRITE setCurrentSpace NOTIFY currentSpaceChanged)
|
||||
|
||||
/**
|
||||
* @brief The RoomListModel that should be used for linear room visualisation.
|
||||
*
|
||||
* The connection 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 the connection
|
||||
* changes.
|
||||
*/
|
||||
Q_PROPERTY(RoomListModel *roomListModel READ roomListModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The SortFilterRoomListModel that should be used for room visualisation.
|
||||
*/
|
||||
Q_PROPERTY(SortFilterRoomListModel *sortFilterRoomListModel READ sortFilterRoomListModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The SortFilterSpaceListModel that should be used for space visualisation.
|
||||
*/
|
||||
Q_PROPERTY(SortFilterSpaceListModel *sortFilterSpaceListModel READ sortFilterSpaceListModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The RoomTreeModel that should be used for room visualisation.
|
||||
*
|
||||
* The connection 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 the connection
|
||||
* changes.
|
||||
*/
|
||||
Q_PROPERTY(RoomTreeModel *roomTreeModel READ roomTreeModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The SortFilterRoomTreeModel that should be used for room visualisation.
|
||||
*/
|
||||
Q_PROPERTY(SortFilterRoomTreeModel *sortFilterRoomTreeModel READ sortFilterRoomTreeModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The TimelineModel that should be used for room message visualisation.
|
||||
*
|
||||
@@ -89,16 +126,6 @@ class RoomManager : public QObject, public UriResolverBase
|
||||
*/
|
||||
Q_PROPERTY(bool hasOpenRoom READ hasOpenRoom NOTIFY currentRoomChanged)
|
||||
|
||||
/**
|
||||
* @brief The room ID of the last space entered.
|
||||
*/
|
||||
Q_PROPERTY(QString lastSpaceId READ lastSpaceId WRITE setLastSpaceId NOTIFY directChatsActiveChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the last SpaceDrawer category selected was direct chats.
|
||||
*/
|
||||
Q_PROPERTY(bool directChatsActive READ directChatsActive WRITE setDirectChatsActive NOTIFY directChatsActiveChanged)
|
||||
|
||||
/**
|
||||
* @brief The ChatDocumentHandler for the open room.
|
||||
*
|
||||
@@ -117,6 +144,12 @@ public:
|
||||
|
||||
NeoChatRoom *currentRoom() const;
|
||||
|
||||
RoomListModel *roomListModel() const;
|
||||
SortFilterRoomListModel *sortFilterRoomListModel() const;
|
||||
SortFilterSpaceListModel *sortFilterSpaceListModel() const;
|
||||
RoomTreeModel *roomTreeModel() const;
|
||||
SortFilterRoomTreeModel *sortFilterRoomTreeModel() const;
|
||||
|
||||
TimelineModel *timelineModel() const;
|
||||
MessageFilterModel *messageFilterModel() const;
|
||||
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
||||
@@ -170,6 +203,8 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void maximizeMedia(int index);
|
||||
|
||||
Q_INVOKABLE void maximizeCode(const QVariantMap &author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
/**
|
||||
* @brief Request that any full screen overlay currently open closes.
|
||||
*/
|
||||
@@ -185,11 +220,6 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText = {});
|
||||
|
||||
/**
|
||||
* @brief Call this when the current used connection is dropped.
|
||||
*/
|
||||
Q_INVOKABLE void reset();
|
||||
|
||||
ChatDocumentHandler *chatDocumentHandler() const;
|
||||
void setChatDocumentHandler(ChatDocumentHandler *handler);
|
||||
|
||||
@@ -198,13 +228,14 @@ public:
|
||||
*/
|
||||
void setUrlArgument(const QString &arg);
|
||||
|
||||
QString lastSpaceId() const;
|
||||
void setLastSpaceId(const QString &lastSpaceId);
|
||||
QString currentSpace() const;
|
||||
|
||||
bool directChatsActive() const;
|
||||
void setDirectChatsActive(bool directChatsActive);
|
||||
|
||||
NeoChatConnection *connection() const;
|
||||
/**
|
||||
* @brief Set the current connection
|
||||
*/
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
Q_SIGNALS:
|
||||
@@ -213,59 +244,11 @@ Q_SIGNALS:
|
||||
|
||||
void currentRoomChanged();
|
||||
|
||||
/**
|
||||
* @brief Push a new room page.
|
||||
*
|
||||
* Signal triggered when the main window pageStack should push a new page with
|
||||
* the message list for the given room.
|
||||
*
|
||||
* @param room the room to be shown on the new page.
|
||||
* @param event the event to got to if available.
|
||||
*/
|
||||
void pushRoom(NeoChatRoom *room, const QString &event);
|
||||
|
||||
/**
|
||||
* @brief Replace the existing room.
|
||||
*
|
||||
* Signal triggered when the room displayed by the message list should be changed.
|
||||
*
|
||||
* @param room the room to be shown on the new page.
|
||||
* @param event the event to got to if available.
|
||||
*/
|
||||
void replaceRoom(NeoChatRoom *room, const QString &event);
|
||||
|
||||
/**
|
||||
* @brief Push a new space home page.
|
||||
*
|
||||
* Signal triggered when the main window pageStack should push a new page with
|
||||
* the space home for the given space room.
|
||||
*
|
||||
* @param spaceRoom the space room to be shown on the new page.
|
||||
*/
|
||||
void pushSpaceHome(NeoChatRoom *spaceRoom);
|
||||
|
||||
/**
|
||||
* @brief Replace the existing space home.
|
||||
*
|
||||
* Signal triggered when the currently displayed room page should be changed
|
||||
* to the space home for the given space room.
|
||||
*
|
||||
* @param spaceRoom the space room to be shown on the new page.
|
||||
*/
|
||||
void replaceSpaceHome(NeoChatRoom *spaceRoom);
|
||||
|
||||
/**
|
||||
* @brief Go to the specified event in the current room.
|
||||
*/
|
||||
void goToEvent(const QString &event);
|
||||
|
||||
/**
|
||||
* @brief Open room in a new window.
|
||||
*
|
||||
* Signal triggered when a room needs to be opened in a new window.
|
||||
*/
|
||||
void openRoomInNewWindow(NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Show details for the given user.
|
||||
*
|
||||
@@ -283,6 +266,11 @@ Q_SIGNALS:
|
||||
*/
|
||||
void showMaximizedMedia(int index);
|
||||
|
||||
/**
|
||||
* @brief Request a block of code is shown maximized.
|
||||
*/
|
||||
void showMaximizedCode(const QVariantMap &author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
/**
|
||||
* @brief Request that any full screen overlay closes.
|
||||
*/
|
||||
@@ -331,15 +319,24 @@ Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
|
||||
void directChatsActiveChanged();
|
||||
void lastSpaceIdChanged();
|
||||
|
||||
void externalUrl(const QUrl &url);
|
||||
|
||||
void currentSpaceChanged();
|
||||
|
||||
private:
|
||||
void openRoomForActiveConnection();
|
||||
|
||||
/** The room currently being shown in the main view (RoomPage.qml). This can be null, if there is no room.
|
||||
* If this is a space, the space home page is shown.
|
||||
*/
|
||||
QPointer<NeoChatRoom> m_currentRoom;
|
||||
NeoChatRoom *m_lastCurrentRoom;
|
||||
|
||||
/** The id of the space currently opened in the space drawer. If this is empty, the uncategorized rooms are shown.
|
||||
* If it is "DM", the direct messages are shown. Otherwise it's the id of a toplevel space.
|
||||
*/
|
||||
QString m_currentSpaceId;
|
||||
|
||||
QString m_arg;
|
||||
KSharedConfig::Ptr m_config;
|
||||
KConfigGroup m_lastRoomConfig;
|
||||
@@ -347,11 +344,22 @@ private:
|
||||
KConfigGroup m_directChatsConfig;
|
||||
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
||||
|
||||
RoomListModel *m_roomListModel;
|
||||
SortFilterRoomListModel *m_sortFilterRoomListModel;
|
||||
SortFilterSpaceListModel *m_sortFilterSpaceListModel;
|
||||
RoomTreeModel *m_roomTreeModel;
|
||||
SortFilterRoomTreeModel *m_sortFilterRoomTreeModel;
|
||||
|
||||
TimelineModel *m_timelineModel;
|
||||
MessageFilterModel *m_messageFilterModel;
|
||||
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
||||
NeoChatConnection *m_connection;
|
||||
|
||||
void setCurrentRoom(const QString &roomId);
|
||||
|
||||
// Space ID, "DM", or empty string
|
||||
void setCurrentSpace(const QString &spaceId, bool setRoom = true);
|
||||
|
||||
/**
|
||||
* @brief Resolve a user URI.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "neochatroom.h"
|
||||
#include "roomlistmodel.h"
|
||||
#include "roommanager.h"
|
||||
#include "sortfilterroomlistmodel.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
RemoteImage Runner::serializeImage(const QImage &image)
|
||||
@@ -28,9 +29,9 @@ RemoteImage Runner::serializeImage(const QImage &image)
|
||||
|
||||
Runner::Runner()
|
||||
: QObject()
|
||||
, m_sourceModel(new RoomListModel(this))
|
||||
, m_model(new SortFilterRoomListModel(m_sourceModel, this))
|
||||
{
|
||||
m_sourceModel = new RoomListModel(this);
|
||||
m_model.setSourceModel(m_sourceModel);
|
||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this]() {
|
||||
m_sourceModel->setConnection(Controller::instance().activeConnection());
|
||||
});
|
||||
@@ -44,13 +45,13 @@ Runner::Runner()
|
||||
|
||||
void Runner::setRoomListModel(RoomListModel *roomListModel)
|
||||
{
|
||||
m_model.setSourceModel(roomListModel);
|
||||
m_model->setSourceModel(roomListModel);
|
||||
Q_EMIT roomListModelChanged();
|
||||
}
|
||||
|
||||
RoomListModel *Runner::roomListModel() const
|
||||
{
|
||||
return dynamic_cast<RoomListModel *>(m_model.sourceModel());
|
||||
return dynamic_cast<RoomListModel *>(m_model->sourceModel());
|
||||
}
|
||||
|
||||
RemoteActions Runner::Actions()
|
||||
@@ -60,22 +61,22 @@ RemoteActions Runner::Actions()
|
||||
|
||||
RemoteMatches Runner::Match(const QString &searchTerm)
|
||||
{
|
||||
m_model.setFilterText(searchTerm);
|
||||
m_model->setFilterText(searchTerm);
|
||||
|
||||
RemoteMatches matches;
|
||||
|
||||
for (int i = 0; i < m_model.rowCount(); ++i) {
|
||||
for (int i = 0; i < m_model->rowCount(); ++i) {
|
||||
RemoteMatch match;
|
||||
|
||||
const QString name = m_model.data(m_model.index(i, 0), RoomListModel::DisplayNameRole).toString();
|
||||
const QString name = m_model->data(m_model->index(i, 0), RoomListModel::DisplayNameRole).toString();
|
||||
|
||||
match.iconName = QStringLiteral("org.kde.neochat");
|
||||
match.id = m_model.data(m_model.index(i, 0), RoomListModel::RoomIdRole).toString();
|
||||
match.id = m_model->data(m_model->index(i, 0), RoomListModel::RoomIdRole).toString();
|
||||
match.text = name;
|
||||
match.relevance = 1;
|
||||
const RemoteImage remoteImage = serializeImage(m_model.data(m_model.index(i, 0), RoomListModel::AvatarImageRole).value<QImage>());
|
||||
const RemoteImage remoteImage = serializeImage(m_model->data(m_model->index(i, 0), RoomListModel::AvatarImageRole).value<QImage>());
|
||||
match.properties.insert(QStringLiteral("icon-data"), QVariant::fromValue(remoteImage));
|
||||
match.properties.insert(QStringLiteral("subtext"), m_model.data(m_model.index(i, 0), RoomListModel::TopicRole).toString());
|
||||
match.properties.insert(QStringLiteral("subtext"), m_model->data(m_model->index(i, 0), RoomListModel::TopicRole).toString());
|
||||
|
||||
if (name.compare(searchTerm, Qt::CaseInsensitive) == 0) {
|
||||
match.type = ExactMatch;
|
||||
|
||||
@@ -203,7 +203,7 @@ Q_SIGNALS:
|
||||
private:
|
||||
RemoteImage serializeImage(const QImage &image);
|
||||
|
||||
SortFilterRoomListModel m_model;
|
||||
RoomListModel *m_sourceModel;
|
||||
QPointer<RoomListModel> m_sourceModel;
|
||||
QPointer<SortFilterRoomListModel> m_model;
|
||||
Runner();
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
qt_add_library(settings STATIC)
|
||||
qt_add_qml_module(settings
|
||||
URI org.kde.neochat.settings
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/settings
|
||||
QML_FILES
|
||||
NeoChatSettings.qml
|
||||
RoomSettings.qml
|
||||
|
||||
@@ -17,6 +17,7 @@ FormCard.AbstractFormDelegate {
|
||||
required property string id
|
||||
required property string timestamp
|
||||
required property string displayName
|
||||
required property int type
|
||||
|
||||
property bool editDeviceName: false
|
||||
property bool showVerifyButton
|
||||
@@ -98,7 +99,7 @@ FormCard.AbstractFormDelegate {
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
visible: root.showVerifyButton
|
||||
visible: root.showVerifyButton && root.type !== DevicesModel.Verified
|
||||
action: Kirigami.Action {
|
||||
id: verifyDeviceAction
|
||||
text: i18n("Verify device")
|
||||
@@ -112,6 +113,21 @@ FormCard.AbstractFormDelegate {
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
Kirigami.Icon {
|
||||
visible: root.showVerifyButton && root.type === DevicesModel.Verified
|
||||
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
implicitHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
source: "security-high"
|
||||
|
||||
HoverHandler {
|
||||
id: verifyHover
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: i18nc("This device is verified", "Verified")
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
visible: verifyHover.hovered
|
||||
}
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.devtools
|
||||
import org.kde.neochat.config
|
||||
|
||||
FormCard.FormCardPage {
|
||||
@@ -218,7 +219,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: Config.developerTools
|
||||
text: i18n("Open developer tools")
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'DevtoolsPage.qml'), {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Developer Tools")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user