Compare commits

..

29 Commits

Author SHA1 Message Date
l10n daemon script
ebd38fb435 GIT_SILENT Sync po/docbooks with svn 2024-11-14 03:10:10 +00:00
l10n daemon script
a5b999e682 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-11-14 03:06:40 +00:00
l10n daemon script
41d34fc0e4 GIT_SILENT Sync po/docbooks with svn 2024-11-13 03:08:49 +00:00
l10n daemon script
b51194f90f SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-11-13 03:05:22 +00:00
l10n daemon script
80ac9e1ba7 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-11-12 03:11:52 +00:00
l10n daemon script
e3874c824a GIT_SILENT Sync po/docbooks with svn 2024-11-11 03:23:01 +00:00
l10n daemon script
6599c6b609 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-11-11 03:15:47 +00:00
l10n daemon script
13d522221c GIT_SILENT made messages (after extraction) 2024-11-11 02:40:16 +00:00
Joshua Goins
dd8f926f32 PollHandler: Make sure it's not constructible from QML
(cherry picked from commit d6b780762e)
2024-11-10 10:29:26 -05:00
Joshua Goins
258312e798 PollHandler: Ensure that m_pollStartEvent is always initialized to null
Otherwise it may be undefined, and we DO create default-constructed
PollHandler. For example, one is used as a fallback poll object
in NeoChatRoom::poll.

This is blind fix for a pretty nasty poll-related crash we saw a few
months ago.

BUG: 493649
(cherry picked from commit 5ef66b5cf6)
2024-11-10 10:29:17 -05:00
Joshua Goins
43d40c7e75 Add m.room.create state events to sync_response
In case we need to access the creation state in an appium test in the
future.

(cherry picked from commit 85ee5084b6)
2024-11-10 07:40:41 -05:00
Joshua Goins
cbcc9a6514 Hide rooms that have a defined room type
I have a room with a custom type that's only for holding data, and
doesn't need to be shown in the room list. Currently the spec is a bit
vague about what clients should do, but hiding them is probably fine
for now.

(cherry picked from commit bb9ce117de)
2024-11-10 07:40:41 -05:00
l10n daemon script
625048610b GIT_SILENT Sync po/docbooks with svn 2024-11-10 03:30:48 +00:00
l10n daemon script
fa47b67e3d SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-11-10 03:16:42 +00:00
Carl Schwan
9347a66acf RoomGeneralPage: Add missing separator
And some other minor fixes

(cherry picked from commit 00c5aa26bb)
2024-11-09 18:12:25 -05:00
Joshua Goins
317df56ffa Make closing link previews instant, as it should be
We were missing a endResetModel() call, now with it added the removal
happens instantly.

(cherry picked from commit bae4de227c)
2024-11-09 18:12:25 -05:00
Joshua Goins
fed9197716 Stop being able to crash NeoChat by pressing a button repeatedly
If you spam click the "Close link preview" button, it's possible to
crash NeoChat. This is because the index check is wrong for the array
size.

It's possible to even do this due to a bug causing the removal to be
reflected visually too slowly, that's fixed in the next commit.

(cherry picked from commit 253f891c5a)
2024-11-09 18:12:25 -05:00
Joshua Goins
1e892599e9 Improve clicking link previews
First of all, clicking on them actually works - because we were missing
an import for RoomManager. Secondly, we use a dedicated TapHandler
since onLinkActivated sucks. We want to be able to click anywhere on the
preview to go to the website/room anyway.

(cherry picked from commit 6966159062)
2024-11-09 18:12:25 -05:00
Joshua Goins
b7229ca0cf Don't set isThread on the message and file delegate context menus
It doesn't have a property called isThread, and I don't know where it
went - if it ever existed?

(cherry picked from commit 07d3b80c3e)
2024-11-09 18:12:25 -05:00
Joshua Goins
953b711823 Make fullscreen images focused when they're opened
Otherwise keyboard shortcuts don't work until you tap the image, which
makes no sense.

BUG: 484322
(cherry picked from commit a41d0f3214)
2024-11-09 18:12:25 -05:00
Joshua Goins
01d903efd3 Fix viewing any kind of data in developer tools
Fix pageStack being undefined, so we're able to view event data again.

(cherry picked from commit 1ee15de78b)
2024-11-09 18:12:25 -05:00
Carl Schwan
241dd81932 Update checkbox of PollComponent
Use FormCheckDelegate instead of a CheckBox inside a RowLayout. This
increase the click area particularly on mobile.

(cherry picked from commit b044358970)
2024-11-09 18:12:24 -05:00
Oliver Beard
f6dfe0cbcf timeline: Round separators for replies and link previews
(cherry picked from commit d2e11bb3bb)
2024-11-09 18:12:24 -05:00
Joshua Goins
f10b97139c README: Change snap store badge to the one from apps.kde.org
It seems CORS is blocking access to the badge, but we have rehosted on
apps.kde.org.

(cherry picked from commit a55bac899c)
2024-11-09 18:12:24 -05:00
Joshua Goins
385c5b3405 Update network proxy page with the improved version from Tokodon
This functions the same, but looks a bit nicer.

(cherry picked from commit c2380fb8df)
2024-11-09 16:28:47 -05:00
Joshua Goins
7bc6f906f8 Update desktop file and app description to match AppStream data
This was updated to "Chat on Matrix" but in other places it was never
switched from "Matrix client" and the like. Now it should be more
consistent.

(cherry picked from commit f31c644b13)
2024-11-09 16:28:47 -05:00
Joshua Goins
b8b1434a95 Clarify that sorting rooms by activity isn't the only thing it does
Recently, it also sorts rooms based on unread notification count and
importance. This adds a clarification to the setting so users (like me)
aren't confused why it isn't sorting only by activity.

(cherry picked from commit 26cd621d0e)
2024-11-09 16:28:47 -05:00
l10n daemon script
85c7a4bcb3 GIT_SILENT Sync po/docbooks with svn 2024-11-09 03:10:22 +00:00
Albert Astals Cid
84b698a7e8 GIT_SILENT Upgrade release service version to 24.11.80. 2024-11-08 19:06:28 +01:00
104 changed files with 8509 additions and 10132 deletions

View File

@@ -7,9 +7,9 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script. # KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "25") set(RELEASE_SERVICE_VERSION_MAJOR "24")
set(RELEASE_SERVICE_VERSION_MINOR "03") set(RELEASE_SERVICE_VERSION_MINOR "11")
set(RELEASE_SERVICE_VERSION_MICRO "70") set(RELEASE_SERVICE_VERSION_MICRO "80")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION}) project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})

View File

@@ -535,7 +535,7 @@ void TextHandlerTest::componentOutput_data()
QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}}; QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}};
QTest::newRow("quote") << QStringLiteral("<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>") QTest::newRow("quote") << QStringLiteral("<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}, << QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Quote, QStringLiteral("blockquote"), {}}}; MessageComponent{MessageComponentType::Quote, QStringLiteral("\"blockquote\""), {}}};
QTest::newRow("no tag first paragraph") << QStringLiteral("Text\n<p>Text</p>") QTest::newRow("no tag first paragraph") << QStringLiteral("Text\n<p>Text</p>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}, << QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}}; MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};

View File

@@ -59,7 +59,6 @@
<summary xml:lang="eu">Berriketa Matrix-en</summary> <summary xml:lang="eu">Berriketa Matrix-en</summary>
<summary xml:lang="fr">Discuter sur Matrix</summary> <summary xml:lang="fr">Discuter sur Matrix</summary>
<summary xml:lang="gl">Charlar en Matrix</summary> <summary xml:lang="gl">Charlar en Matrix</summary>
<summary xml:lang="hu">Csevegés Matrixon</summary>
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary> <summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
<summary xml:lang="it">Chat su Matrix</summary> <summary xml:lang="it">Chat su Matrix</summary>
<summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary> <summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary>
@@ -288,7 +287,6 @@
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value> <value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value> <value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value> <value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
<value key="KDE::supporters">Tanguy Fardet</value>
</custom> </custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable> <launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots> <screenshots>

View File

@@ -90,22 +90,16 @@ GenericName[zh_TW]=Matrix 用戶端
Comment=Chat on Matrix Comment=Chat on Matrix
Comment[ca]=Xat a Matrix Comment[ca]=Xat a Matrix
Comment[ca@valencia]=Xat a Matrix Comment[ca@valencia]=Xat a Matrix
Comment[en_GB]=Chat on Matrix
Comment[es]=Chat en Matrix Comment[es]=Chat en Matrix
Comment[eu]=Berriketa Matrix-en Comment[eu]=Berriketa Matrix-en
Comment[fr]=Clavarder sur Matrix Comment[fr]=Clavarder sur Matrix
Comment[gl]=Charle en Matrix Comment[gl]=Charle en Matrix
Comment[hu]=Csevegés Matrixon Comment[hu]=Csevegés Matrixon
Comment[ia]=Conversation en ditecto sur Matrix
Comment[it]= su Matrix Comment[it]= su Matrix
Comment[ka]=ჩატი Matrix-ზე
Comment[nl]=Chat op Matrix
Comment[pl]=Rozmawiaj na Matriksie Comment[pl]=Rozmawiaj na Matriksie
Comment[sl]=Klepet na Matrixu Comment[sl]=Klepet na Matrixu
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
Comment[tr]=Matrix Üzerinde Sohbet Et Comment[tr]=Matrix Üzerinde Sohbet Et
Comment[uk]=Спілкування у Matrix Comment[uk]=Спілкування у Matrix
Comment[x-test]=xxChat on Matrixxx
MimeType=x-scheme-handler/matrix; MimeType=x-scheme-handler/matrix;
Exec=neochat %u Exec=neochat %u
Terminal=false Terminal=false

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -176,14 +176,13 @@ QQC2.Control {
RowLayout { RowLayout {
QQC2.ScrollView { QQC2.ScrollView {
id: chatBarScrollView id: chatBarScrollView
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumHeight: Kirigami.Units.gridUnit * 8 Layout.maximumHeight: Kirigami.Units.gridUnit * 8
Layout.minimumHeight: Kirigami.Units.gridUnit * 3
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased // HACK: This is to stop the ScrollBar flickering on and off as the height is increased
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
@@ -321,11 +320,12 @@ QQC2.Control {
id: actionsRow id: actionsRow
spacing: 0 spacing: 0
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
Layout.bottomMargin: Kirigami.Units.smallSpacing * 4 Layout.bottomMargin: Kirigami.Units.smallSpacing * 1.5
Repeater { Repeater {
model: root.actions model: root.actions
delegate: QQC2.ToolButton { delegate: QQC2.ToolButton {
Layout.alignment: Qt.AlignVCenter
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source) icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
onClicked: modelData.trigger() onClicked: modelData.trigger()
@@ -342,6 +342,7 @@ QQC2.Control {
} }
} }
} }
DelegateSizeHelper { DelegateSizeHelper {
id: chatBarSizeHelper id: chatBarSizeHelper
startBreakpoint: Kirigami.Units.gridUnit * 46 startBreakpoint: Kirigami.Units.gridUnit * 46

View File

@@ -43,9 +43,6 @@ QQC2.ItemDelegate {
anchors.fill: parent anchors.fill: parent
visible: root.emoji.startsWith("mxc") || root.isImage visible: root.emoji.startsWith("mxc") || root.isImage
source: visible ? root.emoji : "" source: visible ? root.emoji : ""
fillMode: Image.PreserveAspectFit
sourceSize.width: width
sourceSize.height: height
} }
} }

View File

@@ -84,7 +84,6 @@ QQC2.ScrollView {
Kirigami.PlaceholderMessage { Kirigami.PlaceholderMessage {
anchors.centerIn: parent anchors.centerIn: parent
icon.name: root.stickers ? "stickers" : "preferences-desktop-emoticons"
text: root.stickers ? i18n("No stickers") : i18n("No emojis") text: root.stickers ? i18n("No stickers") : i18n("No emojis")
visible: emojis.count === 0 visible: emojis.count === 0
} }

View File

@@ -66,7 +66,6 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: root.categoryIconSize + QQC2.ScrollBar.horizontal.height Layout.preferredHeight: root.categoryIconSize + QQC2.ScrollBar.horizontal.height
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0 QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
visible: categories.count !== 0
ListView { ListView {
id: categories id: categories
@@ -202,13 +201,8 @@ ColumnLayout {
width: root.categoryIconSize width: root.categoryIconSize
height: width height: width
checked: stickerModel.packIndex === model.index checked: stickerModel.packIndex === model.index
padding: Kirigami.Units.largeSpacing
contentItem: Image { contentItem: Image {
source: model.avatarUrl source: model.avatarUrl
fillMode: Image.PreserveAspectFit
sourceSize.width: width
sourceSize.height: height
} }
QQC2.ToolTip.text: model.name QQC2.ToolTip.text: model.name
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay

View File

@@ -13,7 +13,6 @@ LoginStep {
id: root id: root
FormCard.FormTextDelegate { FormCard.FormTextDelegate {
textItem.wrapMode: Text.Wrap
text: i18n("Please wait while your messages are loaded from the server. This might take a little while.") text: i18n("Please wait while your messages are loaded from the server. This might take a little while.")
} }
FormCard.AbstractFormDelegate { FormCard.AbstractFormDelegate {

View File

@@ -7,7 +7,6 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.settings import org.kde.neochat.settings
@@ -91,27 +90,11 @@ Kirigami.Page {
id: loadedAccounts id: loadedAccounts
model: AccountRegistry model: AccountRegistry
delegate: FormCard.FormButtonDelegate { delegate: FormCard.FormButtonDelegate {
id: delegate text: model.userId
required property string userId
required property NeoChatConnection connection
text: QmlUtils.escapeString(connection.localUser.displayName)
description: connection.localUser.id
leadingPadding: Kirigami.Units.largeSpacing
onClicked: { onClicked: {
Controller.activeConnection = delegate.connection; Controller.activeConnection = model.connection;
root.connectionChosen(); root.connectionChosen();
} }
leading: KirigamiComponents.Avatar {
id: avatar
name: delegate.text
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
source: delegate.connection.localUser.avatarUrl.toString().length > 0 ? delegate.connection.makeMediaUrl(delegate.connection.localUser.avatarUrl) : ""
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
}
} }
} }
Repeater { Repeater {

View File

@@ -600,19 +600,14 @@ bool ActionsModel::handleQuickEditAction(NeoChatRoom *room, const QString &messa
} else { } else {
originalString = event->plainBody(); originalString = event->plainBody();
} }
QString replaceId = event->id();
const auto eventRelation = event->relatesTo();
if (eventRelation && eventRelation->type == "m.replace"_L1) {
replaceId = eventRelation->eventId;
}
if (flags == "/g"_L1) { if (flags == "/g"_L1) {
room->postHtmlMessage(messageText, originalString.replace(regex, replacement), event->msgtype(), {}, replaceId); room->postHtmlMessage(messageText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
} else { } else {
room->postHtmlMessage(messageText, room->postHtmlMessage(messageText,
originalString.replace(originalString.indexOf(regex), regex.size(), replacement), originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
event->msgtype(), event->msgtype(),
{}, {},
replaceId); event->id());
} }
return true; return true;
} }

View File

@@ -85,7 +85,13 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole); return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
} }
if (role == IconNameRole) { if (role == IconNameRole) {
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString(); auto mediaId = m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
if (mediaId.isEmpty()) {
return QVariant();
}
if (m_room) {
return m_room->connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(mediaId)));
}
} }
} }
if (m_autoCompletionType == Emoji) { if (m_autoCompletionType == Emoji) {

View File

@@ -34,7 +34,7 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &event
: QAbstractListModel(parent) : QAbstractListModel(parent)
, m_room(room) , m_room(room)
, m_eventId(eventId) , m_eventId(eventId)
, m_currentState(isPending ? Pending : Unknown) , m_isPending(isPending)
, m_isReply(isReply) , m_isReply(isReply)
{ {
initializeModel(); initializeModel();
@@ -45,27 +45,19 @@ void MessageContentModel::initializeModel()
Q_ASSERT(m_room != nullptr); Q_ASSERT(m_room != nullptr);
Q_ASSERT(!m_eventId.isEmpty()); Q_ASSERT(!m_eventId.isEmpty());
connect(m_room, &NeoChatRoom::pendingEventAdded, this, [this]() { connect(this, &MessageContentModel::eventUnavailable, this, &MessageContentModel::getEvent);
if (m_room != nullptr && m_currentState == Unknown) {
initializeEvent();
updateReplyModel();
resetModel();
}
});
connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) { connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) {
if (m_room != nullptr) { if (m_room != nullptr) {
if (m_eventId == serverEvent->id() || m_eventId == serverEvent->transactionId()) { if (m_eventId == serverEvent->id() || m_eventId == serverEvent->transactionId()) {
beginResetModel();
m_isPending = false;
m_eventId = serverEvent->id(); m_eventId = serverEvent->id();
initializeEvent();
endResetModel();
} }
} }
}); });
connect(m_room, &NeoChatRoom::pendingEventMerged, this, [this]() {
if (m_room != nullptr && m_currentState == Pending) {
initializeEvent();
updateReplyModel();
resetModel();
}
});
connect(m_room, &NeoChatRoom::addedMessages, this, [this](int fromIndex, int toIndex) { connect(m_room, &NeoChatRoom::addedMessages, this, [this](int fromIndex, int toIndex) {
if (m_room != nullptr) { if (m_room != nullptr) {
for (int i = fromIndex; i <= toIndex; i++) { for (int i = fromIndex; i <= toIndex; i++) {
@@ -151,33 +143,20 @@ void MessageContentModel::initializeModel()
}); });
initializeEvent(); initializeEvent();
if (m_currentState == Available || m_currentState == Pending) { updateReplyModel();
updateReplyModel();
}
resetModel(); resetModel();
} }
void MessageContentModel::initializeEvent() void MessageContentModel::initializeEvent()
{ {
if (m_currentState == UnAvailable) { const auto event = m_room->getEvent(m_eventId);
if (event == nullptr) {
Q_EMIT eventUnavailable();
return; return;
} }
const auto eventResult = m_room->getEvent(m_eventId);
if (eventResult.first == nullptr) {
if (m_currentState != Pending) {
getEvent();
}
return;
}
if (eventResult.second) {
m_currentState = Pending;
} else {
m_currentState = Available;
}
if (m_eventSenderObject == nullptr) { if (m_eventSenderObject == nullptr) {
auto senderId = eventResult.first->senderId(); auto senderId = event->senderId();
// A pending event might not have a sender ID set yet but in that case it must // A pending event might not have a sender ID set yet but in that case it must
// be the local member. // be the local member.
if (senderId.isEmpty()) { if (senderId.isEmpty()) {
@@ -193,6 +172,7 @@ void MessageContentModel::getEvent()
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) { Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
if (m_room != nullptr) { if (m_room != nullptr) {
if (eventId == m_eventId) { if (eventId == m_eventId) {
m_notFound = false;
initializeEvent(); initializeEvent();
updateReplyModel(); updateReplyModel();
resetModel(); resetModel();
@@ -204,7 +184,7 @@ void MessageContentModel::getEvent()
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventNotFound, this, [this](const QString &eventId) { Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventNotFound, this, [this](const QString &eventId) {
if (m_room != nullptr) { if (m_room != nullptr) {
if (eventId == m_eventId) { if (eventId == m_eventId) {
m_currentState = UnAvailable; m_notFound = true;
resetModel(); resetModel();
return true; return true;
} }
@@ -257,7 +237,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
const auto component = m_components[index.row()]; const auto component = m_components[index.row()];
const auto event = m_room->getEvent(m_eventId); const auto event = m_room->getEvent(m_eventId);
if (event.first == nullptr) { if (event == nullptr) {
if (role == DisplayRole) { if (role == DisplayRole) {
if (m_isReply) { if (m_isReply) {
return i18n("Loading reply"); return i18n("Loading reply");
@@ -272,7 +252,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
} }
if (role == DisplayRole) { if (role == DisplayRole) {
if (m_currentState == UnAvailable || m_room->connection()->isIgnored(m_eventSenderId)) { if (m_notFound || m_room->connection()->isIgnored(m_eventSenderId)) {
Kirigami::Platform::PlatformTheme *theme = Kirigami::Platform::PlatformTheme *theme =
static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true)); static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
@@ -296,7 +276,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
if (!component.content.isEmpty()) { if (!component.content.isEmpty()) {
return component.content; return component.content;
} }
return EventHandler::richBody(m_room, event.first); return EventHandler::richBody(m_room, event);
} }
if (role == ComponentTypeRole) { if (role == ComponentTypeRole) {
return component.type; return component.type;
@@ -305,53 +285,53 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
return component.attributes; return component.attributes;
} }
if (role == EventIdRole) { if (role == EventIdRole) {
return EventHandler::id(event.first); return EventHandler::id(event);
} }
if (role == TimeRole) { if (role == TimeRole) {
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) { const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
return event.first->transactionId() == pendingEvent->transactionId(); return event->transactionId() == pendingEvent->transactionId();
}); });
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated(); auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
return EventHandler::time(event.first, m_currentState == Pending, lastUpdated); return EventHandler::time(event, m_isPending, lastUpdated);
} }
if (role == TimeStringRole) { if (role == TimeStringRole) {
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) { const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
return event.first->transactionId() == pendingEvent->transactionId(); return event->transactionId() == pendingEvent->transactionId();
}); });
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated(); auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
return EventHandler::timeString(event.first, QStringLiteral("hh:mm"), m_currentState == Pending, lastUpdated); return EventHandler::timeString(event, QStringLiteral("hh:mm"), m_isPending, lastUpdated);
} }
if (role == AuthorRole) { if (role == AuthorRole) {
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get()); return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
} }
if (role == MediaInfoRole) { if (role == MediaInfoRole) {
return EventHandler::mediaInfo(m_room, event.first); return EventHandler::mediaInfo(m_room, event);
} }
if (role == FileTransferInfoRole) { if (role == FileTransferInfoRole) {
return QVariant::fromValue(m_room->cachedFileTransferInfo(event.first)); return QVariant::fromValue(m_room->cachedFileTransferInfo(event));
} }
if (role == ItineraryModelRole) { if (role == ItineraryModelRole) {
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel); return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
} }
if (role == LatitudeRole) { if (role == LatitudeRole) {
return EventHandler::latitude(event.first); return EventHandler::latitude(event);
} }
if (role == LongitudeRole) { if (role == LongitudeRole) {
return EventHandler::longitude(event.first); return EventHandler::longitude(event);
} }
if (role == AssetRole) { if (role == AssetRole) {
return EventHandler::locationAssetType(event.first); return EventHandler::locationAssetType(event);
} }
if (role == PollHandlerRole) { if (role == PollHandlerRole) {
return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId)); return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId));
} }
if (role == ReplyEventIdRole) { if (role == ReplyEventIdRole) {
return EventHandler::replyId(event.first); return EventHandler::replyId(event);
} }
if (role == ReplyAuthorRole) { if (role == ReplyAuthorRole) {
return QVariant::fromValue(EventHandler::replyAuthor(m_room, event.first)); return QVariant::fromValue(EventHandler::replyAuthor(m_room, event));
} }
if (role == ReplyContentModelRole) { if (role == ReplyContentModelRole) {
return QVariant::fromValue<MessageContentModel *>(m_replyModel); return QVariant::fromValue<MessageContentModel *>(m_replyModel);
@@ -407,17 +387,18 @@ QHash<int, QByteArray> MessageContentModel::roleNames() const
void MessageContentModel::resetModel() void MessageContentModel::resetModel()
{ {
const auto event = m_room->getEvent(m_eventId);
beginResetModel(); beginResetModel();
m_components.clear(); m_components.clear();
if (m_room->connection()->isIgnored(m_eventSenderId) || m_currentState == UnAvailable) { if (m_room->connection()->isIgnored(m_eventSenderId) || m_notFound) {
m_components += MessageComponent{MessageComponentType::Text, QString(), {}}; m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
endResetModel(); endResetModel();
return; return;
} }
const auto event = m_room->getEvent(m_eventId); if (event == nullptr) {
if (event.first == nullptr) {
m_components += MessageComponent{MessageComponentType::Loading, QString(), {}}; m_components += MessageComponent{MessageComponentType::Loading, QString(), {}};
endResetModel(); endResetModel();
return; return;
@@ -450,19 +431,19 @@ void MessageContentModel::resetContent(bool isEditing, bool isThreading)
QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing, bool isThreading) QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing, bool isThreading)
{ {
const auto event = m_room->getEvent(m_eventId); const auto event = m_room->getEvent(m_eventId);
if (event.first == nullptr) { if (event == nullptr) {
return {}; return {};
} }
QList<MessageComponent> newComponents; QList<MessageComponent> newComponents;
if (eventCast<const Quotient::RoomMessageEvent>(event.first) if (eventCast<const Quotient::RoomMessageEvent>(event)
&& eventCast<const Quotient::RoomMessageEvent>(event.first)->rawMsgtype() == QStringLiteral("m.key.verification.request")) { && eventCast<const Quotient::RoomMessageEvent>(event)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
newComponents += MessageComponent{MessageComponentType::Verification, QString(), {}}; newComponents += MessageComponent{MessageComponentType::Verification, QString(), {}};
return newComponents; return newComponents;
} }
if (event.first->isRedacted()) { if (event->isRedacted()) {
newComponents += MessageComponent{MessageComponentType::Text, QString(), {}}; newComponents += MessageComponent{MessageComponentType::Text, QString(), {}};
return newComponents; return newComponents;
} }
@@ -474,7 +455,7 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
if (isEditing) { if (isEditing) {
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}}; newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
} else { } else {
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event.first))); newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event)));
} }
if (m_room->urlPreviewEnabled()) { if (m_room->urlPreviewEnabled()) {
@@ -482,7 +463,7 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
} }
// If the event is already threaded the ThreadModel will handle displaying a chat bar. // If the event is already threaded the ThreadModel will handle displaying a chat bar.
if (isThreading && !EventHandler::isThreaded(event.first)) { if (isThreading && !EventHandler::isThreaded(event)) {
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}}; newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
} }
@@ -492,11 +473,11 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
void MessageContentModel::updateReplyModel() void MessageContentModel::updateReplyModel()
{ {
const auto event = m_room->getEvent(m_eventId); const auto event = m_room->getEvent(m_eventId);
if (event.first == nullptr || m_isReply) { if (event == nullptr || m_isReply) {
return; return;
} }
if (!EventHandler::hasReply(event.first) || (EventHandler::isThreaded(event.first) && NeoChatConfig::self()->threads())) { if (!EventHandler::hasReply(event) || (EventHandler::isThreaded(event) && NeoChatConfig::self()->threads())) {
if (m_replyModel) { if (m_replyModel) {
delete m_replyModel; delete m_replyModel;
} }
@@ -507,7 +488,7 @@ void MessageContentModel::updateReplyModel()
return; return;
} }
m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(event.first), true, false, this); m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(event), true, false, this);
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() { connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole}); Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
@@ -517,13 +498,13 @@ void MessageContentModel::updateReplyModel()
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type) QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
{ {
const auto event = m_room->getEvent(m_eventId); const auto event = m_room->getEvent(m_eventId);
if (event.first == nullptr) { if (event == nullptr) {
return {}; return {};
} }
switch (type) { switch (type) {
case MessageComponentType::Text: { case MessageComponentType::Text: {
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first); const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
auto body = EventHandler::rawMessageBody(*roomMessageEvent); auto body = EventHandler::rawMessageBody(*roomMessageEvent);
return TextHandler().textComponents(body, return TextHandler().textComponents(body,
EventHandler::messageBodyInputFormat(*roomMessageEvent), EventHandler::messageBodyInputFormat(*roomMessageEvent),
@@ -534,11 +515,11 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
case MessageComponentType::File: { case MessageComponentType::File: {
QList<MessageComponent> components; QList<MessageComponent> components;
components += MessageComponent{MessageComponentType::File, QString(), {}}; components += MessageComponent{MessageComponentType::File, QString(), {}};
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first); const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
if (m_emptyItinerary) { if (m_emptyItinerary) {
if (!m_isReply) { if (!m_isReply) {
auto fileTransferInfo = m_room->cachedFileTransferInfo(event.first); auto fileTransferInfo = m_room->cachedFileTransferInfo(event);
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->has<EventContent::FileContent>()); Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->has<EventContent::FileContent>());
@@ -586,24 +567,17 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
case MessageComponentType::Image: case MessageComponentType::Image:
case MessageComponentType::Audio: case MessageComponentType::Audio:
case MessageComponentType::Video: { case MessageComponentType::Video: {
if (!event.first->is<StickerEvent>()) { if (!event->is<StickerEvent>()) {
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first); const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
const auto fileContent = roomMessageEvent->get<EventContent::FileContentBase>(); QList<MessageComponent> components;
if (fileContent != nullptr) { components += MessageComponent{type, QString(), {}};
const auto fileInfo = fileContent->commonInfo(); auto body = EventHandler::rawMessageBody(*roomMessageEvent);
const auto body = EventHandler::rawMessageBody(*roomMessageEvent); components += TextHandler().textComponents(body,
// Do not attach the description to the image, if it's the same as the original filename. EventHandler::messageBodyInputFormat(*roomMessageEvent),
if (fileInfo.originalName != body) { m_room,
QList<MessageComponent> components; roomMessageEvent,
components += MessageComponent{type, QString(), {}}; roomMessageEvent->isReplaced());
components += TextHandler().textComponents(body, return components;
EventHandler::messageBodyInputFormat(*roomMessageEvent),
m_room,
roomMessageEvent,
roomMessageEvent->isReplaced());
return components;
}
}
} }
} }
default: default:
@@ -679,13 +653,13 @@ void MessageContentModel::closeLinkPreview(int row)
void MessageContentModel::updateItineraryModel() void MessageContentModel::updateItineraryModel()
{ {
const auto event = m_room->getEvent(m_eventId); const auto event = m_room->getEvent(m_eventId);
if (m_room == nullptr || event.first == nullptr) { if (m_room == nullptr || event == nullptr) {
return; return;
} }
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first)) { if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
if (roomMessageEvent->has<EventContent::FileContent>()) { if (roomMessageEvent->has<EventContent::FileContent>()) {
auto filePath = m_room->cachedFileTransferInfo(event.first).localPath; auto filePath = m_room->cachedFileTransferInfo(event).localPath;
if (filePath.isEmpty() && m_itineraryModel != nullptr) { if (filePath.isEmpty() && m_itineraryModel != nullptr) {
delete m_itineraryModel; delete m_itineraryModel;
m_itineraryModel = nullptr; m_itineraryModel = nullptr;

View File

@@ -31,14 +31,6 @@ class MessageContentModel : public QAbstractListModel
Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged) Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged)
public: public:
enum MessageState {
Unknown, /**< The message state is unknown. */
Pending, /**< The message is a new pending message which the server has not yet acknowledged. */
Available, /**< The message is available and acknowledged by the server. */
UnAvailable, /**< The message can't be retrieved either because it doesn't exist or is blocked. */
};
Q_ENUM(MessageState)
/** /**
* @brief Defines the model roles. * @brief Defines the model roles.
*/ */
@@ -106,6 +98,7 @@ public:
Q_SIGNALS: Q_SIGNALS:
void showAuthorChanged(); void showAuthorChanged();
void eventUnavailable();
void eventUpdated(); void eventUpdated();
private: private:
@@ -114,9 +107,10 @@ private:
QString m_eventSenderId; QString m_eventSenderId;
std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr; std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr;
MessageState m_currentState = Unknown; bool m_isPending;
bool m_showAuthor = true; bool m_showAuthor = true;
bool m_isReply; bool m_isReply;
bool m_notFound = false;
void initializeModel(); void initializeModel();
void initializeEvent(); void initializeEvent();

View File

@@ -160,21 +160,12 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
refreshLastUserEvents(i); refreshLastUserEvents(i);
} }
}); });
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 0)
connect(m_currentRoom, &Room::pendingEventAdded, this, [this](const Quotient::RoomEvent *event) {
m_initialized = true;
createEventObjects(event, true);
beginInsertRows({}, 0, 0);
endInsertRows();
});
#else
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) { connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
m_initialized = true; m_initialized = true;
createEventObjects(event, true); createEventObjects(event);
beginInsertRows({}, 0, 0); beginInsertRows({}, 0, 0);
}); });
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows); connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
#endif
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) { connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole}); Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
if (i == 0) { if (i == 0) {
@@ -627,7 +618,7 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex(); return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex();
} }
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event, bool isPending) void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
{ {
if (event == nullptr) { if (event == nullptr) {
return; return;
@@ -650,7 +641,7 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event, boo
if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) { if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) {
if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) { if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, eventId, false, isPending)); m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, eventId));
} }
} }

View File

@@ -136,7 +136,7 @@ private:
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {}); int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
void moveReadMarker(const QString &toEventId); void moveReadMarker(const QString &toEventId);
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false); void createEventObjects(const Quotient::RoomEvent *event);
// Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows // Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
bool m_initialized = false; bool m_initialized = false;

View File

@@ -212,7 +212,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
return room->displayName().toHtmlEscaped(); return room->displayName().toHtmlEscaped();
} }
if (role == AvatarRole) { if (role == AvatarRole) {
return room->avatarMediaUrl(); return room->avatarMediaId();
} }
if (role == CanonicalAliasRole) { if (role == CanonicalAliasRole) {
return room->canonicalAlias(); return room->canonicalAlias();

View File

@@ -324,7 +324,7 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
return room->displayName(); return room->displayName();
} }
if (role == AvatarRole) { if (role == AvatarRole) {
return room->avatarMediaUrl(); return room->avatarMediaId();
} }
if (role == CanonicalAliasRole) { if (role == CanonicalAliasRole) {
return room->canonicalAlias(); return room->canonicalAlias();

View File

@@ -431,9 +431,9 @@ QDateTime NeoChatRoom::lastActiveTime()
return messageEvents().rbegin()->get()->originTimestamp(); return messageEvents().rbegin()->get()->originTimestamp();
} }
QUrl NeoChatRoom::avatarMediaUrl() const QString NeoChatRoom::avatarMediaId() const
{ {
if (const auto avatar = Room::avatarUrl(); !avatar.isEmpty()) { if (const auto avatar = Room::avatarMediaId(); !avatar.isEmpty()) {
return avatar; return avatar;
} }
@@ -441,7 +441,7 @@ QUrl NeoChatRoom::avatarMediaUrl() const
const auto directChatMembers = this->directChatMembers(); const auto directChatMembers = this->directChatMembers();
for (const auto member : directChatMembers) { for (const auto member : directChatMembers) {
if (member != localMember()) { if (member != localMember()) {
return member.avatarUrl(); return member.avatarMediaId();
} }
} }
@@ -1749,31 +1749,25 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
}); });
} }
std::pair<const Quotient::RoomEvent *, bool> NeoChatRoom::getEvent(const QString &eventId) const const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const
{ {
if (eventId.isEmpty()) { if (eventId.isEmpty()) {
return {}; return nullptr;
} }
const auto timelineIt = findInTimeline(eventId); const auto timelineIt = findInTimeline(eventId);
if (timelineIt != historyEdge()) { if (timelineIt != historyEdge()) {
return std::make_pair(timelineIt->get(), false); return timelineIt->get();
} }
auto pendingIt = findPendingEvent(eventId); const auto pendingIt = findPendingEvent(eventId);
if (pendingIt != pendingEvents().end()) { if (pendingIt != pendingEvents().end()) {
return std::make_pair(pendingIt->event(), true); return pendingIt->event();
}
// findPendingEvent() searches by transaction ID, we also need to check event ID.
for (const auto &event : pendingEvents()) {
if (event->id() == eventId || event->transactionId() == eventId) {
return std::make_pair(event.event(), true);
}
} }
auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt<Quotient::RoomEvent> &event) { auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt<Quotient::RoomEvent> &event) {
return event->id() == eventId; return event->id() == eventId;
}); });
return std::make_pair(extraIt != m_extraEvents.end() ? extraIt->get() : nullptr, false); return extraIt != m_extraEvents.end() ? extraIt->get() : nullptr;
} }
const RoomEvent *NeoChatRoom::getReplyForEvent(const RoomEvent &event) const const RoomEvent *NeoChatRoom::getReplyForEvent(const RoomEvent &event) const

View File

@@ -69,9 +69,9 @@ class NeoChatRoom : public Quotient::Room
Q_PROPERTY(bool readMarkerLoaded READ readMarkerLoaded NOTIFY readMarkerLoadedChanged) Q_PROPERTY(bool readMarkerLoaded READ readMarkerLoaded NOTIFY readMarkerLoadedChanged)
/** /**
* @brief The avatar image to be used for the room, as a mxc:// URL. * @brief The avatar image to be used for the room.
*/ */
Q_PROPERTY(QUrl avatarMediaUrl READ avatarMediaUrl NOTIFY avatarChanged STORED false) Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false)
/** /**
* @brief Get a RoomMember object for the other person in a direct chat. * @brief Get a RoomMember object for the other person in a direct chat.
@@ -320,7 +320,7 @@ public:
[[nodiscard]] bool readMarkerLoaded() const; [[nodiscard]] bool readMarkerLoaded() const;
[[nodiscard]] QUrl avatarMediaUrl() const; [[nodiscard]] QString avatarMediaId() const;
NeochatRoomMember *directChatRemoteMember(); NeochatRoomMember *directChatRemoteMember();
@@ -570,7 +570,7 @@ public:
* *
* The result will be nullptr if not found so needs to be managed. * The result will be nullptr if not found so needs to be managed.
*/ */
std::pair<const Quotient::RoomEvent *, bool> getEvent(const QString &eventId) const; const Quotient::RoomEvent *getEvent(const QString &eventId) const;
/** /**
* @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply. * @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply.

View File

@@ -153,6 +153,15 @@ QColor NeochatRoomMember::color() const
return m_room->member(m_memberId).color(); return m_room->member(m_memberId).color();
} }
QString NeochatRoomMember::avatarMediaId() const
{
if (m_room == nullptr || m_memberId.isEmpty()) {
return {};
}
return m_room->member(m_memberId).avatarMediaId();
}
QUrl NeochatRoomMember::avatarUrl() const QUrl NeochatRoomMember::avatarUrl() const
{ {
if (m_room == nullptr || m_memberId.isEmpty()) { if (m_room == nullptr || m_memberId.isEmpty()) {

View File

@@ -70,6 +70,7 @@ public:
int hue() const; int hue() const;
qreal hueF() const; qreal hueF() const;
QColor color() const; QColor color() const;
QString avatarMediaId() const;
QUrl avatarUrl() const; QUrl avatarUrl() const;
Q_SIGNALS: Q_SIGNALS:

View File

@@ -27,8 +27,7 @@ QQC2.Menu {
text: "https://matrix.to/#/" + root.connection.localUser.id, text: "https://matrix.to/#/" + root.connection.localUser.id,
title: root.connection.localUser.displayName, title: root.connection.localUser.displayName,
subtitle: root.connection.localUser.id, subtitle: root.connection.localUser.id,
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl. avatarSource: root.connection.makeMediaUrl(root.connection.localUser.avatarUrl)
avatarSource: root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : ""
}); });
if (typeof root.closeDialog === "function") { if (typeof root.closeDialog === "function") {
root.closeDialog(); root.closeDialog();

View File

@@ -125,7 +125,7 @@ Kirigami.Dialog {
width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
} }
source: userDelegate.connection.localUser.avatarUrl.toString().length > 0 ? userDelegate.connection.makeMediaUrl(userDelegate.connection.localUser.avatarUrl) : "" source: userDelegate.connection.localUser.avatarMediaId ? userDelegate.connection.makeMediaUrl("mxc://" + userDelegate.connection.localUser.avatarMediaId) : ""
name: userDelegate.connection.localUser.displayName ?? userDelegate.connection.localUser.id name: userDelegate.connection.localUser.displayName ?? userDelegate.connection.localUser.id
} }

View File

@@ -18,7 +18,7 @@ QQC2.ItemDelegate {
required property NeoChatRoom currentRoom required property NeoChatRoom currentRoom
required property bool categoryVisible required property bool categoryVisible
required property string filterText required property string filterText
required property url avatar required property string avatar
required property string displayName required property string displayName
topPadding: Kirigami.Units.largeSpacing topPadding: Kirigami.Units.largeSpacing
@@ -32,7 +32,7 @@ QQC2.ItemDelegate {
visible: root.categoryVisible || filterText.length > 0 visible: root.categoryVisible || filterText.length > 0
contentItem: KirigamiComponents.Avatar { contentItem: KirigamiComponents.Avatar {
source: root.avatar source: root.avatar ? root.currentRoom.connection.makeMediaUrl("mxc://" + root.avatar) : ""
name: root.displayName name: root.displayName
sourceSize { sourceSize {

View File

@@ -27,17 +27,38 @@ Loader {
Component { Component {
id: regularMenu id: regularMenu
QQC2.Menu { QQC2.Menu {
QQC2.MenuItem {
text: room.isFavourite ? i18n("Remove from Favorites") : i18n("Add to Favorites")
icon.name: room.isFavourite ? "bookmark-remove" : "bookmark-new"
onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
}
QQC2.MenuItem {
text: room.isLowPriority ? i18n("Reprioritize") : i18n("Deprioritize")
icon.name: room.isLowPriority ? "arrow-up-symbolic" : "arrow-down-symbolic"
onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", 1.0)
}
QQC2.MenuItem { QQC2.MenuItem {
text: i18n("Mark as Read") text: i18n("Mark as Read")
icon.name: "checkmark" icon.name: "checkmark"
enabled: room.notificationCount > 0
onTriggered: room.markAllMessagesAsRead() onTriggered: room.markAllMessagesAsRead()
} }
QQC2.MenuSeparator {} QQC2.MenuItem {
text: room.isDirectChat() ? i18nc("@action:inmenu", "Copy user's Matrix ID to Clipboard") : i18nc("@action:inmenu", "Copy Address to Clipboard")
icon.name: "edit-copy"
onTriggered: if (room.isDirectChat()) {
Clipboard.saveText(room.directChatRemoteMember.id);
} else if (room.canonicalAlias.length === 0) {
Clipboard.saveText(room.id);
} else {
Clipboard.saveText(room.canonicalAlias);
}
}
QQC2.Menu { QQC2.Menu {
title: i18nc("@action:inmenu", "Notifications") title: i18n("Notification State")
icon.name: "notifications" icon.name: "notifications"
QQC2.MenuItem { QQC2.MenuItem {
@@ -86,32 +107,6 @@ Loader {
} }
} }
QQC2.MenuItem {
text: room.isFavourite ? i18n("Remove from Favorites") : i18n("Add to Favorites")
icon.name: room.isFavourite ? "rating" : "rating-unrated"
onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
}
QQC2.MenuItem {
text: room.isLowPriority ? i18n("Reprioritize") : i18n("Deprioritize")
icon.name: room.isLowPriority ? "arrow-up-symbolic" : "arrow-down-symbolic"
onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", 1.0)
}
QQC2.MenuSeparator {}
QQC2.MenuItem {
text: room.isDirectChat() ? i18nc("@action:inmenu", "Copy user's Matrix ID to Clipboard") : i18nc("@action:inmenu", "Copy Address to Clipboard")
icon.name: "edit-copy"
onTriggered: if (room.isDirectChat()) {
Clipboard.saveText(room.directChatRemoteMember.id);
} else if (room.canonicalAlias.length === 0) {
Clipboard.saveText(room.id);
} else {
Clipboard.saveText(room.canonicalAlias);
}
}
QQC2.MenuItem { QQC2.MenuItem {
text: i18nc("@action:inmenu", "Room Settings") text: i18nc("@action:inmenu", "Room Settings")
icon.name: 'settings-configure-symbolic' icon.name: 'settings-configure-symbolic'
@@ -168,7 +163,7 @@ Loader {
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar { KirigamiComponents.Avatar {
id: avatar id: avatar
source: room.avatarMediaUrl source: room.avatarMediaId ? root.connection.makeMediaUrl("mxc://" + room.avatarMediaId) : ""
name: room.displayName name: room.displayName
Layout.preferredWidth: Kirigami.Units.gridUnit * 3 Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.preferredHeight: Kirigami.Units.gridUnit * 3 Layout.preferredHeight: Kirigami.Units.gridUnit * 3

View File

@@ -5,7 +5,6 @@
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import QtQuick.Layouts import QtQuick.Layouts
import Qt.labs.qmlmodels
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
@@ -178,25 +177,10 @@ Loader {
Repeater { Repeater {
model: root.actions model: root.actions
DelegateChooser { QQC2.MenuItem {
role: "separator" visible: modelData.visible
DelegateChoice { action: modelData
roleValue: true onClicked: root.item.close()
QQC2.MenuSeparator {
visible: modelData.visible
}
}
DelegateChoice {
roleValue: false
QQC2.MenuItem {
visible: modelData.visible
action: modelData
onClicked: root.item.close()
}
}
} }
} }
QQC2.Menu { QQC2.Menu {
@@ -357,30 +341,15 @@ Loader {
id: listViewAction id: listViewAction
model: root.actions model: root.actions
DelegateChooser { FormCard.FormButtonDelegate {
role: "separator" icon.name: modelData.icon.name
DelegateChoice { icon.color: modelData.icon.color ?? undefined
roleValue: true enabled: modelData.enabled
visible: modelData.visible
FormCard.FormDelegateSeparator { text: modelData.text
visible: modelData.visible onClicked: {
} modelData.triggered();
} root.item.close();
DelegateChoice {
roleValue: false
FormCard.FormButtonDelegate {
icon.name: modelData.icon.name
icon.color: modelData.icon.color ?? undefined
enabled: modelData.enabled
visible: modelData.visible
text: modelData.text
onClicked: {
modelData.triggered();
root.item.close();
}
}
} }
} }
} }

View File

@@ -38,7 +38,7 @@ ColumnLayout {
contentItem: KirigamiComponents.Avatar { contentItem: KirigamiComponents.Avatar {
name: root.room ? root.room.displayName : "" name: root.room ? root.room.displayName : ""
source: root.room ? root.room.avatarMediaUrl : "" source: root.room ? root.room.connection.makeMediaUrl("mxc://" + root.room.avatarMediaId) : ""
Rectangle { Rectangle {
visible: root.room.usesEncryption visible: root.room.usesEncryption

View File

@@ -42,36 +42,30 @@ DelegateContextMenu {
* Each action will be instantiated as a single line in the menu. * Each action will be instantiated as a single line in the menu.
*/ */
property list<Kirigami.Action> actions: [ property list<Kirigami.Action> actions: [
DelegateContextMenu.ReplyMessageAction {},
Kirigami.Action { Kirigami.Action {
separator: true text: i18n("Open Externally")
},
Kirigami.Action {
text: i18nc("@action:inmenu", "Open Image")
icon.name: "document-open" icon.name: "document-open"
onTriggered: { onTriggered: {
currentRoom.openEventMediaExternally(root.eventId); currentRoom.openEventMediaExternally(root.eventId);
} }
}, },
Kirigami.Action { Kirigami.Action {
text: i18nc("@action:inmenu", "Save Image…") text: i18n("Save As")
icon.name: "document-save" icon.name: "document-save"
onTriggered: { onTriggered: {
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay); var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
dialog.selectedFile = currentRoom.fileNameToDownload(eventId);
dialog.open(); dialog.open();
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId);
} }
}, },
DelegateContextMenu.ReplyMessageAction {},
Kirigami.Action { Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Image") text: i18n("Copy")
icon.name: "edit-copy" icon.name: "edit-copy"
onTriggered: { onTriggered: {
currentRoom.copyEventMedia(root.eventId); currentRoom.copyEventMedia(root.eventId);
} }
}, },
Kirigami.Action {
separator: true
},
Kirigami.Action { Kirigami.Action {
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact") visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
text: i18n("Remove") text: i18n("Remove")
@@ -94,13 +88,7 @@ DelegateContextMenu {
}, },
DelegateContextMenu.ReportMessageAction {}, DelegateContextMenu.ReportMessageAction {},
DelegateContextMenu.ShowUserAction {}, DelegateContextMenu.ShowUserAction {},
Kirigami.Action { DelegateContextMenu.ViewSourceAction {}
separator: true
visible: viewSourceAction.visible
},
DelegateContextMenu.ViewSourceAction {
id: viewSourceAction
}
] ]
/** /**

View File

@@ -102,7 +102,7 @@ Labs.MenuBar {
} }
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "About KDE") text: i18nc("menu", "About KDE")
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDEPage")) onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDE"))
} }
} }
} }

View File

@@ -34,7 +34,7 @@ ColumnLayout {
Layout.preferredHeight: Kirigami.Units.iconSizes.large Layout.preferredHeight: Kirigami.Units.iconSizes.large
name: root.room ? root.room.displayName : "" name: root.room ? root.room.displayName : ""
source: root.room ? root.room.avatarMediaUrl : "" source: root.room ? root.room.connection.makeMediaUrl("mxc://" + root.room.avatarMediaId) : ""
Rectangle { Rectangle {
visible: room.usesEncryption visible: room.usesEncryption
@@ -75,7 +75,6 @@ ColumnLayout {
textFormat: TextEdit.PlainText textFormat: TextEdit.PlainText
visible: root.room && root.room.canonicalAlias visible: root.room && root.room.canonicalAlias
text: root.room && root.room.canonicalAlias ? root.room.canonicalAlias : "" text: root.room && root.room.canonicalAlias ? root.room.canonicalAlias : ""
color: Kirigami.Theme.disabledTextColor
} }
} }
QQC2.AbstractButton { QQC2.AbstractButton {
@@ -93,7 +92,7 @@ ColumnLayout {
text: barcode.content, text: barcode.content,
title: root.room ? root.room.displayName : "", title: root.room ? root.room.displayName : "",
subtitle: root.room ? root.room.id : "", subtitle: root.room ? root.room.id : "",
avatarSource: root.room ? root.room.avatarMediaUrl : "" avatarSource: root.room && root.room.avatarMediaId ? root.room.connection.makeMediaUrl("mxc://" + root.room.avatarMediaId) : ""
}); });
map.open(); map.open();
} }

View File

@@ -7,6 +7,7 @@ import QtQuick.Layouts
import QtCore as Core import QtCore as Core
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kquickimageeditor as KQuickImageEditor import org.kde.kquickimageeditor as KQuickImageEditor
Kirigami.Page { Kirigami.Page {
@@ -167,11 +168,10 @@ Kirigami.Page {
} }
} }
footer: Kirigami.InlineMessage { footer: KirigamiComponents.Banner {
id: msg id: msg
type: Kirigami.MessageType.Error type: Kirigami.MessageType.Error
showCloseButton: true showCloseButton: true
visible: false visible: false
position: Kirigami.InlineMessage.Position.Header
} }
} }

View File

@@ -44,7 +44,7 @@ DelegateContextMenu {
}, },
DelegateContextMenu.ReplyMessageAction {}, DelegateContextMenu.ReplyMessageAction {},
Kirigami.Action { Kirigami.Action {
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward") text: i18nc("@action:inmenu As in 'Forward this message'", "Forward")
icon.name: "mail-forward-symbolic" icon.name: "mail-forward-symbolic"
onTriggered: { onTriggered: {
let page = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), { let page = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
@@ -59,33 +59,21 @@ DelegateContextMenu {
}); });
} }
}, },
Kirigami.Action {
separator: true
},
DelegateContextMenu.RemoveMessageAction {}, DelegateContextMenu.RemoveMessageAction {},
Kirigami.Action { Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Text") text: i18n("Copy")
icon.name: "edit-copy" icon.name: "edit-copy"
onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText) onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
}, },
DelegateContextMenu.ReportMessageAction {},
DelegateContextMenu.ShowUserAction {},
DelegateContextMenu.ViewSourceAction {},
Kirigami.Action { Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Message Link") text: i18n("Copy Link")
icon.name: "edit-copy" icon.name: "edit-copy"
onTriggered: { onTriggered: {
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId); Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
} }
},
Kirigami.Action {
separator: true
},
DelegateContextMenu.ReportMessageAction {},
DelegateContextMenu.ShowUserAction {},
Kirigami.Action {
separator: true
visible: viewSourceAction.visible
},
DelegateContextMenu.ViewSourceAction {
id: viewSourceAction
} }
] ]
} }

View File

@@ -110,8 +110,8 @@ Components.AlbumMaximizeComponent {
onSaveItem: { onSaveItem: {
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay); var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
dialog.selectedFile = currentRoom.fileNameToDownload(root.currentEventId);
dialog.open(); dialog.open();
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(root.currentEventId);
} }
Connections { Connections {

View File

@@ -12,7 +12,7 @@ Components.AbstractMaximizeComponent {
required property string text required property string text
property color avatarColor property color avatarColor
required property url avatarSource required property string avatarSource
onOpened: forceActiveFocus() onOpened: forceActiveFocus()

View File

@@ -30,9 +30,9 @@ Kirigami.Dialog {
FormCard.AbstractFormDelegate { FormCard.AbstractFormDelegate {
background: null background: null
contentItem: RowLayout { contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing * 4
Avatar { Avatar {
source: SpaceHierarchyCache.recommendedSpaceAvatar.toString().length > 0 ? root.connection.makeMediaUrl(SpaceHierarchyCache.recommendedSpaceAvatar) : 0 source: root.connection.makeMediaUrl(SpaceHierarchyCache.recommendedSpaceAvatar)
name: SpaceHierarchyCache.recommendedSpaceDisplayName name: SpaceHierarchyCache.recommendedSpaceDisplayName
} }
ColumnLayout { ColumnLayout {
@@ -51,7 +51,6 @@ Kirigami.Dialog {
FormCard.FormDelegateSeparator {} FormCard.FormDelegateSeparator {}
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Join") text: i18nc("@action:button", "Join")
icon.name: "list-add-symbolic"
onClicked: { onClicked: {
SpaceHierarchyCache.recommendedSpaceHidden = true; SpaceHierarchyCache.recommendedSpaceHidden = true;
RoomManager.resolveResource(SpaceHierarchyCache.recommendedSpaceId, "join"); RoomManager.resolveResource(SpaceHierarchyCache.recommendedSpaceId, "join");
@@ -59,7 +58,6 @@ Kirigami.Dialog {
} }
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
icon.name: "mail-thread-ignored-symbolic"
text: i18nc("@action:button", "Ignore") text: i18nc("@action:button", "Ignore")
onClicked: { onClicked: {
SpaceHierarchyCache.recommendedSpaceHidden = true; SpaceHierarchyCache.recommendedSpaceHidden = true;

View File

@@ -21,7 +21,7 @@ Delegates.RoundedItemDelegate {
required property bool hasHighlightNotifications required property bool hasHighlightNotifications
required property NeoChatRoom currentRoom required property NeoChatRoom currentRoom
required property NeoChatConnection connection required property NeoChatConnection connection
required property url avatar required property string avatar
required property string subtitleText required property string subtitleText
required property string displayName required property string displayName
@@ -55,7 +55,7 @@ Delegates.RoundedItemDelegate {
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing
AvatarNotification { AvatarNotification {
source: root.avatar source: root.avatar ? root.connection.makeMediaUrl("mxc://" + root.avatar) : ""
name: root.displayName name: root.displayName
visible: NeoChatConfig.showAvatarInRoomDrawer visible: NeoChatConfig.showAvatarInRoomDrawer
implicitHeight: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2) implicitHeight: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)

View File

@@ -99,11 +99,8 @@ Kirigami.OverlayDrawer {
Layout.preferredHeight: pageStack.globalToolBar.preferredHeight Layout.preferredHeight: pageStack.globalToolBar.preferredHeight
contentItem: RowLayout { contentItem: RowLayout {
spacing: 0
Kirigami.Heading { Kirigami.Heading {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing
text: drawerItemLoader.item ? drawerItemLoader.item.title : "" text: drawerItemLoader.item ? drawerItemLoader.item.title : ""
} }

View File

@@ -39,16 +39,13 @@ QQC2.ScrollView {
/** /**
* @brief The title that should be displayed for this component if available. * @brief The title that should be displayed for this component if available.
*/ */
readonly property string title: root.room.isSpace ? i18nc("@action:title", "Space Members") : i18nc("@action:title", "Room Information") readonly property string title: root.room.isSpace ? i18nc("@action:title", "Space Members") : i18nc("@action:title", "Room information")
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890) // HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
ListView { ListView {
id: userList id: userList
topMargin: Kirigami.Units.largeSpacing
leftMargin: Kirigami.Units.largeSpacing
rightMargin: Kirigami.Units.largeSpacing
header: ColumnLayout { header: ColumnLayout {
id: columnLayout id: columnLayout
@@ -60,6 +57,7 @@ QQC2.ScrollView {
Loader { Loader {
active: true active: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing
visible: !root.room.isSpace visible: !root.room.isSpace
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
onItemChanged: if (item) { onItemChanged: if (item) {
@@ -135,7 +133,7 @@ QQC2.ScrollView {
Delegates.RoundedItemDelegate { Delegates.RoundedItemDelegate {
id: leaveButton id: leaveButton
icon.name: "arrow-left-symbolic" icon.name: "arrow-left-symbolic"
text: root.room.isSpace ? i18nc("@action:button", "Leave this space") : i18nc("@action:button", "Leave this room") text: i18nc("@action:button", "Leave this room")
activeFocusOnTab: true activeFocusOnTab: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -211,7 +209,7 @@ QQC2.ScrollView {
section.delegate: Kirigami.ListSectionHeader { section.delegate: Kirigami.ListSectionHeader {
required property string section required property string section
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin width: ListView.view.width
text: section text: section
} }
@@ -221,12 +219,11 @@ QQC2.ScrollView {
required property int index required property int index
required property string name required property string name
required property string userId required property string userId
required property url avatar required property string avatar
required property int powerLevel required property int powerLevel
required property string powerLevelString required property string powerLevelString
implicitHeight: Kirigami.Units.gridUnit * 2 implicitHeight: Kirigami.Units.gridUnit * 2
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
text: name text: name

View File

@@ -7,6 +7,7 @@ import QtQuick.Controls as QQC2
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kitemmodels import org.kde.kitemmodels
@@ -98,12 +99,11 @@ Kirigami.Page {
} }
} }
header: Kirigami.InlineMessage { header: KirigamiComponents.Banner {
id: banner id: banner
showCloseButton: true showCloseButton: true
visible: false visible: false
position: Kirigami.InlineMessage.Position.Header
} }
Loader { Loader {

View File

@@ -182,7 +182,7 @@ QQC2.Control {
id: spaceDelegate id: spaceDelegate
required property string displayName required property string displayName
required property url avatar required property string avatar
required property string roomId required property string roomId
required property var currentRoom required property var currentRoom
@@ -191,7 +191,7 @@ QQC2.Control {
Layout.maximumHeight: width - Kirigami.Units.smallSpacing Layout.maximumHeight: width - Kirigami.Units.smallSpacing
text: displayName text: displayName
source: avatar source: avatar ? root.connection.makeMediaUrl("mxc://" + avatar) : ""
notificationCount: spaceDelegate.currentRoom.childrenNotificationCount notificationCount: spaceDelegate.currentRoom.childrenNotificationCount
notificationHighlight: spaceDelegate.currentRoom.childrenHaveHighlightNotifications notificationHighlight: spaceDelegate.currentRoom.childrenHaveHighlightNotifications
@@ -219,7 +219,7 @@ QQC2.Control {
visible: SpaceHierarchyCache.recommendedSpaceId.length > 0 && !root.connection.room(SpaceHierarchyCache.recommendedSpaceId) && !SpaceHierarchyCache.recommendedSpaceHidden visible: SpaceHierarchyCache.recommendedSpaceId.length > 0 && !root.connection.room(SpaceHierarchyCache.recommendedSpaceId) && !SpaceHierarchyCache.recommendedSpaceHidden
text: i18nc("Join <name of a space>", "Join %1", SpaceHierarchyCache.recommendedSpaceDisplayName) text: i18nc("Join <name of a space>", "Join %1", SpaceHierarchyCache.recommendedSpaceDisplayName)
source: SpaceHierarchyCache.recommendedSpaceAvatar.toString().length > 0 ? root.connection.makeMediaUrl(SpaceHierarchyCache.recommendedSpaceAvatar) : "" source: SpaceHierarchyCache.recommendedSpaceAvatar.length > 0 ? root.connection.makeMediaUrl(SpaceHierarchyCache.recommendedSpaceAvatar) : ""
onSelected: { onSelected: {
recommendedSpaceDialogComponent.createObject(QQC2.Overlay.overlay, { recommendedSpaceDialogComponent.createObject(QQC2.Overlay.overlay, {
connection: root.connection connection: root.connection

View File

@@ -62,7 +62,7 @@ ColumnLayout {
onClicked: _private.createRoom(root.currentRoom.id) onClicked: _private.createRoom(root.currentRoom.id)
} }
QQC2.Button { QQC2.Button {
text: i18nc("@action:button", "Leave this space") text: i18nc("@button", "Leave the space")
icon.name: "go-previous" icon.name: "go-previous"
onClicked: RoomManager.leaveRoom(root.currentRoom) onClicked: RoomManager.leaveRoom(root.currentRoom)
} }

View File

@@ -96,7 +96,7 @@ Loader {
KirigamiComponents.Avatar { KirigamiComponents.Avatar {
id: avatar id: avatar
source: room.avatarMediaUrl source: room.avatarMediaId ? root.room.connection.makeMediaUrl("mxc://" + room.avatarMediaId) : ""
Layout.preferredWidth: Kirigami.Units.gridUnit * 3 Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.preferredHeight: Kirigami.Units.gridUnit * 3 Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop

View File

@@ -7,6 +7,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
@@ -19,12 +20,11 @@ FormCard.FormCardPage {
leftPadding: 0 leftPadding: 0
rightPadding: 0 rightPadding: 0
header: Kirigami.InlineMessage { header: KirigamiComponents.Banner {
id: banner id: banner
showCloseButton: true showCloseButton: true
visible: false visible: false
type: Kirigami.MessageType.Error type: Kirigami.MessageType.Error
position: Kirigami.InlineMessage.Position.Header
} }
property SSSSHandler ssssHandler: SSSSHandler { property SSSSHandler ssssHandler: SSSSHandler {

View File

@@ -75,8 +75,6 @@ Kirigami.Dialog {
QQC2.AbstractButton { QQC2.AbstractButton {
Layout.minimumHeight: avatar.height * 0.75 Layout.minimumHeight: avatar.height * 0.75
Layout.maximumHeight: avatar.height * 1.5 Layout.maximumHeight: avatar.height * 1.5
Layout.maximumWidth: avatar.height * 1.5
contentItem: Barcode { contentItem: Barcode {
id: barcode id: barcode
barcodeType: Barcode.QRCode barcodeType: Barcode.QRCode

View File

@@ -27,7 +27,7 @@ RowLayout {
Layout.topMargin: Kirigami.Units.smallSpacing Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 3 : -1 Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 2 : -1
onVisibleChanged: { onVisibleChanged: {
if (!visible) { if (!visible) {
@@ -37,15 +37,14 @@ RowLayout {
} }
KirigamiComponents.AvatarButton { KirigamiComponents.AvatarButton {
id: accountButton id: accountButton
readonly property url avatarUrl: root.connection.localUser.avatarUrl readonly property string mediaId: root.connection.localUser.avatarMediaId
Layout.preferredWidth: Kirigami.Units.iconSizes.medium Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.leftMargin: Kirigami.Units.largeSpacing Layout.leftMargin: Kirigami.Units.largeSpacing
text: i18n("Edit this account") text: i18n("Edit this account")
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl. source: mediaId ? root.connection.makeMediaUrl("mxc://" + mediaId) : ""
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
name: root.connection.localUser.displayName name: root.connection.localUser.displayName
activeFocusOnTab: true activeFocusOnTab: true

View File

@@ -134,15 +134,16 @@ void RoomManager::activateUserModel()
m_userListModel->activate(); m_userListModel->activate();
} }
void RoomManager::resolveResource(const QString &idOrUri, const QString &action) UriResolveResult RoomManager::resolveResource(const Uri &uri)
{ {
resolveResource(Uri{idOrUri}, action); return UriResolverBase::visitResource(m_connection, uri);
} }
void RoomManager::resolveResource(Uri uri, const QString &action) void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
{ {
Uri uri{idOrUri};
if (!uri.isValid()) { if (!uri.isValid()) {
Q_EMIT showMessage(MessageType::Warning, i18n("Malformed or empty Matrix id<br />%1 is not a correct Matrix identifier", uri.toDisplayString())); Q_EMIT showMessage(MessageType::Warning, i18n("Malformed or empty Matrix id<br />%1 is not a correct Matrix identifier", idOrUri));
return; return;
} }
@@ -236,6 +237,7 @@ void RoomManager::loadInitialRoom()
} }
if (m_isMobile) { if (m_isMobile) {
// We still need to remember the last space on mobile
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false); setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
// We don't want to open a room on startup on mobile // We don't want to open a room on startup on mobile
return; return;
@@ -258,7 +260,6 @@ void RoomManager::openRoomForActiveConnection()
setCurrentSpace({}, false); setCurrentSpace({}, false);
return; return;
} }
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
const auto &lastRoom = m_lastRoomConfig.readEntry(m_connection->userId(), QString()); const auto &lastRoom = m_lastRoomConfig.readEntry(m_connection->userId(), QString());
if (lastRoom.isEmpty() || !m_connection->room(lastRoom)) { if (lastRoom.isEmpty() || !m_connection->room(lastRoom)) {
setCurrentRoom({}); setCurrentRoom({});
@@ -266,6 +267,7 @@ void RoomManager::openRoomForActiveConnection()
m_currentRoom = nullptr; m_currentRoom = nullptr;
resolveResource(lastRoom); resolveResource(lastRoom);
} }
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
} }
UriResolveResult RoomManager::visitUser(User *user, const QString &action) UriResolveResult RoomManager::visitUser(User *user, const QString &action)
@@ -313,9 +315,7 @@ void RoomManager::visitRoom(Room *r, const QString &eventId)
// It's important that we compare room *objects* here, not just room *ids*, since we need to deal with the object changing when going invite -> joined // It's important that we compare room *objects* here, not just room *ids*, since we need to deal with the object changing when going invite -> joined
if (m_currentRoom && m_currentRoom == room) { if (m_currentRoom && m_currentRoom == room) {
if (!eventId.isEmpty()) { Q_EMIT goToEvent(eventId);
Q_EMIT goToEvent(eventId);
}
} else { } else {
setCurrentRoom(room->id()); setCurrentRoom(room->id());
} }

View File

@@ -168,6 +168,16 @@ public:
UserListModel *userListModel() const; UserListModel *userListModel() const;
Q_INVOKABLE void activateUserModel(); Q_INVOKABLE void activateUserModel();
/**
* @brief Resolve the given URI resource.
*
* @note It's actually Quotient::UriResolverBase::visitResource() but with Q_INVOKABLE
* and the connection grabbed from RoomManager.
*
* @sa Quotient::UriResolverBase::visitResource()
*/
Q_INVOKABLE UriResolveResult resolveResource(const Uri &uri);
/** /**
* @brief Resolve the given resource. * @brief Resolve the given resource.
* *
@@ -178,16 +188,6 @@ public:
*/ */
Q_INVOKABLE void resolveResource(const QString &idOrUri, const QString &action = {}); Q_INVOKABLE void resolveResource(const QString &idOrUri, const QString &action = {});
/**
* @brief Resolve the given resource URI.
*
* @note It's actually Quotient::UriResolverBase::visitResource() but with Q_INVOKABLE
* and the connection grabbed from RoomManager.
*
* @sa Quotient::UriResolverBase::visitResource()
*/
Q_INVOKABLE void resolveResource(Uri uri, const QString &action = {});
bool hasOpenRoom() const; bool hasOpenRoom() const;
/** /**

View File

@@ -34,8 +34,7 @@ FormCard.FormCardPage {
padding: 0 padding: 0
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl. source: root.connection && root.connection.localUser.avatarMediaId ? root.connection.makeMediaUrl("mxc://" + root.connection.localUser.avatarMediaId) : ""
source: root.connection && (root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : "")
name: root.connection.localUser.displayName name: root.connection.localUser.displayName
onClicked: { onClicked: {
@@ -123,8 +122,7 @@ FormCard.FormCardPage {
text: "https://matrix.to/#/" + root.connection.localUser.id, text: "https://matrix.to/#/" + root.connection.localUser.id,
title: root.connection.localUser.displayName, title: root.connection.localUser.displayName,
subtitle: root.connection.localUser.id, subtitle: root.connection.localUser.id,
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl. avatarSource: root.connection.makeMediaUrl(root.connection.localUser.avatarUrl)
avatarSource: root.connection && (root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : "")
}); });
if (typeof root.closeDialog === "function") { if (typeof root.closeDialog === "function") {
root.closeDialog(); root.closeDialog();

View File

@@ -53,8 +53,7 @@ FormCard.FormCardPage {
contentItem: RowLayout { contentItem: RowLayout {
KirigamiComponents.Avatar { KirigamiComponents.Avatar {
name: accountDelegate.connection.localUser.displayName name: accountDelegate.connection.localUser.displayName
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl. source: accountDelegate.connection.localUser.avatarMediaId ? accountDelegate.connection.makeMediaUrl("mxc://" + accountDelegate.connection.localUser.avatarMediaId) : ""
source: accountDelegate.connection.localUser.avatarUrl.toString().length > 0 ? accountDelegate.connection.makeMediaUrl(accountDelegate.connection.localUser.avatarUrl) : ""
Layout.rightMargin: Kirigami.Units.largeSpacing Layout.rightMargin: Kirigami.Units.largeSpacing
implicitWidth: Kirigami.Units.iconSizes.medium implicitWidth: Kirigami.Units.iconSizes.medium

View File

@@ -8,6 +8,7 @@ import QtQuick.Dialogs
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
@@ -18,12 +19,11 @@ FormCard.FormCardPage {
required property NeoChatConnection connection required property NeoChatConnection connection
header: Kirigami.InlineMessage { header: KirigamiComponents.Banner {
id: banner id: banner
showCloseButton: true showCloseButton: true
visible: false visible: false
type: Kirigami.MessageType.Error type: Kirigami.MessageType.Error
position: Kirigami.InlineMessage.Position.Header
} }
FormCard.FormCard { FormCard.FormCard {

View File

@@ -23,7 +23,7 @@ FormCard.FormCardPage {
} }
FormCard.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 4 Layout.topMargin: Kirigami.Units.largeSpacing
FormCard.FormCheckDelegate { FormCard.FormCheckDelegate {
text: i18n("Enable notifications for this account") text: i18n("Enable notifications for this account")
description: i18n("Whether push notifications are generated by your Matrix server") description: i18n("Whether push notifications are generated by your Matrix server")

View File

@@ -8,6 +8,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
@@ -20,12 +21,11 @@ FormCard.FormCardPage {
title: i18nc("@title", "Import Keys") title: i18nc("@title", "Import Keys")
header: Kirigami.InlineMessage { header: KirigamiComponents.Banner {
id: banner id: banner
showCloseButton: true showCloseButton: true
visible: false visible: false
type: Kirigami.MessageType.Error type: Kirigami.MessageType.Error
position: Kirigami.InlineMessage.Position.Header
} }
FormCard.FormCard { FormCard.FormCard {

View File

@@ -7,6 +7,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
@@ -17,16 +18,15 @@ FormCard.FormCardPage {
title: i18nc("@title", "Security & Safety") title: i18nc("@title", "Security & Safety")
header: Kirigami.InlineMessage { header: KirigamiComponents.Banner {
id: banner id: banner
showCloseButton: true showCloseButton: true
visible: false visible: false
type: Kirigami.MessageType.Error type: Kirigami.MessageType.Error
position: Kirigami.InlineMessage.Position.Header
} }
FormCard.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 4 Layout.topMargin: Kirigami.Units.largeSpacing
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
id: ignoredUsersDelegate id: ignoredUsersDelegate
text: i18nc("@action:button", "Ignored Users") text: i18nc("@action:button", "Ignored Users")

View File

@@ -20,7 +20,7 @@ KirigamiSettings.ConfigurationView {
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "general" moduleId: "general"
text: i18n("General") text: i18n("General")
icon.name: "org.kde.neochat.tray" icon.name: "org.kde.neochat"
page: () => Qt.createComponent("org.kde.neochat.settings", "NeoChatGeneralPage") page: () => Qt.createComponent("org.kde.neochat.settings", "NeoChatGeneralPage")
initialProperties: () => { initialProperties: () => {
return { return {
@@ -31,13 +31,13 @@ KirigamiSettings.ConfigurationView {
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "appearance" moduleId: "appearance"
text: i18n("Appearance") text: i18n("Appearance")
icon.name: "preferences-desktop-theme-global-symbolic" icon.name: "preferences-desktop-theme-global"
page: () => Qt.createComponent("org.kde.neochat.settings", "AppearanceSettingsPage") page: () => Qt.createComponent("org.kde.neochat.settings", "AppearanceSettingsPage")
}, },
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "notifications" moduleId: "notifications"
text: i18n("Notifications") text: i18n("Notifications")
icon.name: "preferences-desktop-notification-symbolic" icon.name: "preferences-desktop-notification"
page: () => Qt.createComponent("org.kde.neochat.settings", "GlobalNotificationsPage") page: () => Qt.createComponent("org.kde.neochat.settings", "GlobalNotificationsPage")
initialProperties: () => { initialProperties: () => {
return { return {
@@ -49,7 +49,7 @@ KirigamiSettings.ConfigurationView {
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "security" moduleId: "security"
text: i18nc("@title", "Security & Safety") text: i18nc("@title", "Security & Safety")
icon.name: "preferences-security-symbolic" icon.name: "preferences-security"
page: () => Qt.createComponent("org.kde.neochat.settings", "NeoChatSecurityPage") page: () => Qt.createComponent("org.kde.neochat.settings", "NeoChatSecurityPage")
initialProperties: () => { initialProperties: () => {
return { return {
@@ -61,14 +61,14 @@ KirigamiSettings.ConfigurationView {
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "accounts" moduleId: "accounts"
text: i18n("Accounts") text: i18n("Accounts")
icon.name: "preferences-system-users-symbolic" icon.name: "preferences-system-users"
page: () => Qt.createComponent("org.kde.neochat.settings", "AccountsPage") page: () => Qt.createComponent("org.kde.neochat.settings", "AccountsPage")
visible: root.connection !== null visible: root.connection !== null
}, },
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "emoticons" moduleId: "emoticons"
text: i18n("Stickers & Emojis") text: i18n("Stickers & Emojis")
icon.name: "preferences-desktop-emoticons-symbolic" icon.name: "preferences-desktop-emoticons"
page: () => Qt.createComponent("org.kde.neochat.settings", "EmoticonsPage") page: () => Qt.createComponent("org.kde.neochat.settings", "EmoticonsPage")
initialProperties: () => { initialProperties: () => {
return { return {
@@ -81,13 +81,13 @@ KirigamiSettings.ConfigurationView {
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "networkProxy" moduleId: "networkProxy"
text: i18n("Network Proxy") text: i18n("Network Proxy")
icon.name: "network-connect-symbolic" icon.name: "network-connect"
page: () => Qt.createComponent("org.kde.neochat.settings", "NetworkProxyPage") page: () => Qt.createComponent("org.kde.neochat.settings", "NetworkProxyPage")
}, },
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "devices" moduleId: "devices"
text: i18n("Devices") text: i18n("Devices")
icon.name: "computer-symbolic" icon.name: "computer"
page: () => Qt.createComponent("org.kde.neochat.settings", "DevicesPage") page: () => Qt.createComponent("org.kde.neochat.settings", "DevicesPage")
initialProperties: () => { initialProperties: () => {
return { return {
@@ -99,15 +99,15 @@ KirigamiSettings.ConfigurationView {
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "aboutNeochat" moduleId: "aboutNeochat"
text: i18n("About NeoChat") text: i18n("About NeoChat")
icon.name: "help-about-symbolic" icon.name: "help-about"
page: () => Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutPage") page: () => Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutPage")
category: i18nc("@title:group", "About") category: i18nc("@title:group", "About")
}, },
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "aboutKDE" moduleId: "aboutKDE"
text: i18n("About KDE") text: i18n("About KDE")
icon.name: "kde-symbolic" icon.name: "kde"
page: () => Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDEPage") page: () => Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDE")
category: i18nc("@title:group", "About") category: i18nc("@title:group", "About")
} }
] ]

View File

@@ -16,7 +16,7 @@ FormCard.FormCardPage {
property bool proxyConfigChanged: false property bool proxyConfigChanged: false
FormCard.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 4 Layout.topMargin: Kirigami.Units.largeSpacing
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
id: systemDefault id: systemDefault

View File

@@ -169,7 +169,7 @@ FormCard.FormCardPage {
id: userListItem id: userListItem
required property string userId required property string userId
required property url avatar required property string avatar
required property string name required property string name
required property int powerLevel required property int powerLevel
required property string powerLevelString required property string powerLevelString
@@ -180,7 +180,7 @@ FormCard.FormCardPage {
KirigamiComponents.Avatar { KirigamiComponents.Avatar {
Layout.preferredWidth: Kirigami.Units.iconSizes.medium Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium Layout.preferredHeight: Kirigami.Units.iconSizes.medium
source: userListItem.avatar source: userListItem.avatar ? root.room.connection.makeMediaUrl(userListItem.avatar) : ""
name: userListItem.name name: userListItem.name
} }

View File

@@ -26,7 +26,7 @@ FormCard.FormCardPage {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Kirigami.Units.gridUnit Layout.topMargin: Kirigami.Units.gridUnit
name: room.name name: room.name
source: room.avatarMediaUrl source: room.avatarMediaId ? root.connection.makeMediaUrl("mxc://" + room.avatarMediaId) : ""
implicitWidth: Kirigami.Units.iconSizes.enormous implicitWidth: Kirigami.Units.iconSizes.enormous
implicitHeight: Kirigami.Units.iconSizes.enormous implicitHeight: Kirigami.Units.iconSizes.enormous

Some files were not shown because too many files have changed in this diff Show More