diff --git a/.appveyor.yml b/.appveyor.yml index 5d28a9f51..f5a1a70c0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,10 +1,10 @@ -image: Visual Studio 2017 +image: Visual Studio 2019 environment: DEPLOY_DIR: Spectral-%APPVEYOR_BUILD_VERSION% matrix: - - QTDIR: C:\Qt\5.12\msvc2017_64 - VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" + - QTDIR: C:\Qt\5.15\msvc2019_64 + VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" PLATFORM: init: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9637c84f3..6759aab3b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ build-appimage: - sudo cmake --build build --target install - cd .. - git clone https://github.com/commonmark/cmark.git && cd cmark - - git checkout tags/0.29.0 + - git reset --hard tags/0.29.0 - cmake . -Bbuild -LA -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install -DCMARK_SHARED=ON -DCMARK_STATIC=ON -DCMARK_TESTS=OFF - cmake --build build --target install --parallel $(nproc) - cd .. diff --git a/imports/Spectral/Component/AutoTextField.qml b/imports/Spectral/Component/AutoTextField.qml index 134470942..ebcd037d3 100644 --- a/imports/Spectral/Component/AutoTextField.qml +++ b/imports/Spectral/Component/AutoTextField.qml @@ -26,7 +26,7 @@ TextField { states: [ State { name: "shown" - when: textField.text.length !== 0 || textField.activeFocus + when: textField.text.length != 0 || textField.activeFocus PropertyChanges { target: floatingPlaceholder; scale: 0.8 } PropertyChanges { target: floatingPlaceholder; anchors.topMargin: -floatingPlaceholder.height * 0.4 } } diff --git a/imports/Spectral/Component/Timeline/AudioDelegate.qml b/imports/Spectral/Component/Timeline/AudioDelegate.qml index 2a7d563b5..87d2a9119 100644 --- a/imports/Spectral/Component/Timeline/AudioDelegate.qml +++ b/imports/Spectral/Component/Timeline/AudioDelegate.qml @@ -96,7 +96,7 @@ RowLayout { } Label { - readonly property int duration: content.info.duration || audio.duration || 0 + readonly property int duration: content.info.duration ?? audio.duration ?? 0 Layout.fillWidth: true @@ -193,14 +193,12 @@ RowLayout { } } - function openSavedFile() - { + function openSavedFile() { if (Qt.openUrlExternally(progressInfo.localPath)) return; if (Qt.openUrlExternally(progressInfo.localDir)) return; } - function humanSize(duration) - { + function humanSize(duration) { if (!duration) return qsTr("Unknown", "Unknown duration") if (duration < 1000) diff --git a/imports/Spectral/Component/Timeline/MessageDelegate.qml b/imports/Spectral/Component/Timeline/MessageDelegate.qml index 6024c1e76..7ee6de063 100644 --- a/imports/Spectral/Component/Timeline/MessageDelegate.qml +++ b/imports/Spectral/Component/Timeline/MessageDelegate.qml @@ -12,8 +12,14 @@ RowLayout { default property alias innerObject : column.children readonly property bool sentByMe: author.isLocalUser - readonly property bool replyVisible: reply || false - readonly property bool failed: marks === EventStatus.SendingFailed + readonly property bool darkBackground: !sentByMe + readonly property bool replyVisible: reply ?? false + readonly property bool failed: marks == EventStatus.SendingFailed + readonly property color authorColor: eventType == "notice" ? MPalette.primary : author.color + readonly property color replyAuthorColor: replyVisible ? reply.author.color : MPalette.accent + + signal saveFileAs() + signal openExternally() id: root diff --git a/imports/Spectral/Component/Timeline/ReactionDelegate.qml b/imports/Spectral/Component/Timeline/ReactionDelegate.qml index 38a393fda..1a9f85117 100644 --- a/imports/Spectral/Component/Timeline/ReactionDelegate.qml +++ b/imports/Spectral/Component/Timeline/ReactionDelegate.qml @@ -4,7 +4,7 @@ import QtQuick.Layouts 1.12 import Spectral.Setting 0.1 Flow { - visible: (reaction && reaction.length > 0) || false + visible: (reaction && reaction.length > 0) ?? false spacing: 8 diff --git a/imports/Spectral/Component/Timeline/VideoDelegate.qml b/imports/Spectral/Component/Timeline/VideoDelegate.qml index abc8f7717..bd22120c6 100644 --- a/imports/Spectral/Component/Timeline/VideoDelegate.qml +++ b/imports/Spectral/Component/Timeline/VideoDelegate.qml @@ -120,7 +120,7 @@ RowLayout { } Image { - readonly property bool isThumbnail: !(content.info.thumbnail_info == null || content.thumbnailMediaId == null) + readonly property bool isThumbnail: content.info.thumbnail_info && content.thumbnailMediaId readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info anchors.fill: parent diff --git a/imports/Spectral/Dialog/AccountDetailDialog.qml b/imports/Spectral/Dialog/AccountDetailDialog.qml index 75b598b8a..a72d31da5 100644 --- a/imports/Spectral/Dialog/AccountDetailDialog.qml +++ b/imports/Spectral/Dialog/AccountDetailDialog.qml @@ -39,7 +39,7 @@ Dialog { height: 48 source: user.avatarMediaId - hint: user.displayName || "No Name" + hint: user.displayName ?? "" Menu { id: contextMenu diff --git a/imports/Spectral/Dialog/RoomSettingsDialog.qml b/imports/Spectral/Dialog/RoomSettingsDialog.qml index 10e4889d8..db8cda7da 100644 --- a/imports/Spectral/Dialog/RoomSettingsDialog.qml +++ b/imports/Spectral/Dialog/RoomSettingsDialog.qml @@ -232,7 +232,7 @@ Dialog { Layout.preferredWidth: 100 wrapMode: Label.Wrap - text: "Main Alias" + text: "Canonical Alias" color: MPalette.lighter } @@ -243,12 +243,12 @@ Dialog { enabled: canChangeCanonicalAlias - model: room.remoteAliases + model: room.aliases - currentIndex: room.remoteAliases.indexOf(room.canonicalAlias) + currentIndex: room.aliases.indexOf(room.canonicalAlias) onCurrentIndexChanged: { - if (room.canonicalAlias != room.remoteAliases[currentIndex]) { - room.setCanonicalAlias(room.remoteAliases[currentIndex]) + if (room.canonicalAlias != room.aliases[currentIndex]) { + room.setCanonicalAlias(room.aliases[currentIndex]) } } } @@ -257,12 +257,14 @@ Dialog { RowLayout { Layout.fillWidth: true + visible: room.altAliases && room.altAliases.length + Label { Layout.preferredWidth: 100 Layout.alignment: Qt.AlignTop wrapMode: Label.Wrap - text: "Local Aliases" + text: "Alt Aliases" color: MPalette.lighter } @@ -272,7 +274,7 @@ Dialog { spacing: 0 Repeater { - model: room.localAliases + model: room.altAliases delegate: RowLayout { Layout.maximumWidth: parent.width @@ -302,42 +304,6 @@ Dialog { } } } - - RowLayout { - Layout.fillWidth: true - - Label { - Layout.preferredWidth: 100 - Layout.alignment: Qt.AlignTop - - wrapMode: Label.Wrap - text: "Remote Aliases" - color: MPalette.lighter - } - - ColumnLayout { - Layout.fillWidth: true - - spacing: 0 - - Repeater { - model: { - var localAliases = room.localAliases - var remoteAliases = room.remoteAliases - return remoteAliases.filter(n => !localAliases.includes(n)) - } - - delegate: Label { - width: parent.width - - text: modelData - - font.pixelSize: 12 - color: MPalette.lighter - } - } - } - } } } } diff --git a/imports/Spectral/Effect/ElevationEffect.qml b/imports/Spectral/Effect/ElevationEffect.qml index 0299fd5c2..d5aa66505 100644 --- a/imports/Spectral/Effect/ElevationEffect.qml +++ b/imports/Spectral/Effect/ElevationEffect.qml @@ -135,7 +135,7 @@ Item { glowRadius: modelData.blur/2 spread: 0.05 color: _shadowColors[index] - cornerRadius: modelData.blur + (effect.sourceItem.radius || 0) + cornerRadius: modelData.blur + (effect.sourceItem.radius ?? 0) } } diff --git a/imports/Spectral/Panel/RoomListPanel.qml b/imports/Spectral/Panel/RoomListPanel.qml index df6676420..2801dc501 100644 --- a/imports/Spectral/Panel/RoomListPanel.qml +++ b/imports/Spectral/Panel/RoomListPanel.qml @@ -108,11 +108,6 @@ Kirigami.ScrollablePage { Layout.fillWidth: true Layout.fillHeight: true - text: name || "No Name" - font.pixelSize: 16 - font.bold: unreadCount >= 0 - elide: Text.ElideRight - wrapMode: Text.NoWrap } Controls.Label { diff --git a/imports/Spectral/Panel/RoomPanelInput.qml b/imports/Spectral/Panel/RoomPanelInput.qml index 256f9dd46..a3ffa1ebe 100644 --- a/imports/Spectral/Panel/RoomPanelInput.qml +++ b/imports/Spectral/Panel/RoomPanelInput.qml @@ -130,9 +130,9 @@ Control { keyNavigationWraps: true delegate: Control { - property string autoCompleteText: modelData.displayName || modelData.unicode + property string autoCompleteText: modelData.displayName ?? modelData.unicode property bool isEmoji: modelData.unicode != null - readonly property bool highlighted: autoCompleteListView.currentIndex === index + readonly property bool highlighted: autoCompleteListView.currentIndex == index height: 36 padding: 6 @@ -140,7 +140,7 @@ Control { background: Rectangle { visible: !isEmoji color: highlighted ? border.color : "transparent" - border.color: isEmoji ? Material.accent : modelData.color + border.color: isEmoji ? MPalette.accent : modelData.color border.width: 2 radius: height / 2 } @@ -164,7 +164,7 @@ Control { Layout.preferredHeight: 24 visible: !isEmoji - source: modelData.avatarMediaId || null + source: modelData.avatarMediaId ?? null color: modelData.color ? Qt.darker(modelData.color, 1.1) : MPalette.accent } diff --git a/include/libQuotient b/include/libQuotient index 89573ca2a..55d8d9c07 160000 --- a/include/libQuotient +++ b/include/libQuotient @@ -1 +1 @@ -Subproject commit 89573ca2a1c19d79c1417e64613f1a0fa696837c +Subproject commit 55d8d9c073b732e296c72bc8a747bbd691931209 diff --git a/src/matriximageprovider.h b/src/matriximageprovider.h index 030ea41ad..70f911e85 100644 --- a/src/matriximageprovider.h +++ b/src/matriximageprovider.h @@ -56,7 +56,7 @@ class MatrixImageProvider : public QObject, public QQuickAsyncImageProvider { Quotient::Connection* connection() { return m_connection; } void setConnection(Quotient::Connection* connection) { - m_connection.store(connection); + m_connection.storeRelaxed(connection); emit connectionChanged(); } diff --git a/src/messageeventmodel.cpp b/src/messageeventmodel.cpp index 91c393bbf..edc73b17c 100644 --- a/src/messageeventmodel.cpp +++ b/src/messageeventmodel.cpp @@ -203,13 +203,13 @@ int MessageEventModel::refreshEventRoles(const QString& id, } inline bool hasValidTimestamp(const Quotient::TimelineItem& ti) { - return ti->timestamp().isValid(); + return ti->originTimestamp().isValid(); } QDateTime MessageEventModel::makeMessageTimestamp( const Quotient::Room::rev_iter_t& baseIt) const { const auto& timeline = m_currentRoom->messageEvents(); - auto ts = baseIt->event()->timestamp(); + auto ts = baseIt->event()->originTimestamp(); if (ts.isValid()) return ts; @@ -218,10 +218,10 @@ QDateTime MessageEventModel::makeMessageTimestamp( using Quotient::TimelineItem; auto rit = std::find_if(baseIt, timeline.rend(), hasValidTimestamp); if (rit != timeline.rend()) - return {rit->event()->timestamp().date(), {0, 0}, Qt::LocalTime}; + return {rit->event()->originTimestamp().date(), {0, 0}, Qt::LocalTime}; auto it = std::find_if(baseIt.base(), timeline.end(), hasValidTimestamp); if (it != timeline.end()) - return {it->event()->timestamp().date(), {0, 0}, Qt::LocalTime}; + return {it->event()->originTimestamp().date(), {0, 0}, Qt::LocalTime}; // What kind of room is that?.. qCritical() << "No valid timestamps in the room timeline!"; @@ -238,7 +238,8 @@ QString MessageEventModel::renderDate(QDateTime timestamp) const { return tr("The day before yesterday"); if (date > QDate::currentDate().addDays(-7)) return date.toString("dddd"); - return date.toString(Qt::DefaultLocaleShortDate); + + return QLocale::system().toString(date, QLocale::ShortFormat); } void MessageEventModel::refreshLastUserEvents(int baseTimelineRow) { diff --git a/src/notifications/manager.h b/src/notifications/manager.h index 5e7888fc6..8b6973bd5 100644 --- a/src/notifications/manager.h +++ b/src/notifications/manager.h @@ -6,7 +6,7 @@ #include #include -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #include #include #endif @@ -25,8 +25,9 @@ class NotificationsManager : public QObject { void notificationClicked(const QString roomId, const QString eventId); private: -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) QDBusInterface dbus; + bool serverSupportsHtml = false; uint showNotification(const QString summary, const QString text, const QImage image); @@ -49,7 +50,7 @@ class NotificationsManager : public QObject { const QImage& icon); }; -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) QDBusArgument& operator<<(QDBusArgument& arg, const QImage& image); const QDBusArgument& operator>>(const QDBusArgument& arg, QImage&); #endif diff --git a/src/notifications/managerlinux.cpp b/src/notifications/managerlinux.cpp index af9f8b94c..a87dc56bb 100644 --- a/src/notifications/managerlinux.cpp +++ b/src/notifications/managerlinux.cpp @@ -5,6 +5,7 @@ #include #include #include +#include NotificationsManager::NotificationsManager(QObject *parent) : QObject(parent), @@ -13,6 +14,15 @@ NotificationsManager::NotificationsManager(QObject *parent) this) { qDBusRegisterMetaType(); + const QDBusReply capabilitiesReply = dbus.call("GetCapabilities"); + + if (capabilitiesReply.isValid()) { + const QStringList capabilities = capabilitiesReply.value(); + serverSupportsHtml = capabilities.contains("body-markup"); + } else { + qWarning() << "Could not get notification server capabilities" << capabilitiesReply.error(); + } + QDBusConnection::sessionBus().connect( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "ActionInvoked", this, @@ -54,14 +64,17 @@ uint NotificationsManager::showNotification(const QString summary, croppedImage = image; } + const QString body = serverSupportsHtml ? text.toHtmlEscaped() : text; + QVariantMap hints; hints["image-data"] = croppedImage; + hints["desktop-entry"] = "org.eu.encom.spectral"; QList argumentList; argumentList << "Spectral"; // app_name argumentList << uint(0); // replace_id argumentList << ""; // app_icon argumentList << summary; // summary - argumentList << text; // body + argumentList << body; // body argumentList << (QStringList("default") << "reply"); // actions argumentList << hints; // hints argumentList << int(-1); // timeout in ms diff --git a/src/publicroomlistmodel.cpp b/src/publicroomlistmodel.cpp index e8bc0d317..29ff97d4d 100644 --- a/src/publicroomlistmodel.cpp +++ b/src/publicroomlistmodel.cpp @@ -110,15 +110,14 @@ void PublicRoomListModel::next(int count) { attempted = true; if (job->status() == BaseJob::Success) { - auto resp = job->data(); - nextBatch = resp.nextBatch; + nextBatch = job->nextBatch(); this->beginInsertRows({}, rooms.count(), - rooms.count() + resp.chunk.count() - 1); - rooms.append(resp.chunk); + rooms.count() + job->chunk().count() - 1); + rooms.append(job->chunk()); this->endInsertRows(); - if (resp.nextBatch.isEmpty()) { + if (job->nextBatch().isEmpty()) { emit hasMoreChanged(); } } diff --git a/src/spectralroom.cpp b/src/spectralroom.cpp index 471bda1d0..3c8f5847d 100644 --- a/src/spectralroom.cpp +++ b/src/spectralroom.cpp @@ -167,7 +167,7 @@ void SpectralRoom::countChanged() { QDateTime SpectralRoom::lastActiveTime() const { if (timelineSize() == 0) return QDateTime(); - return messageEvents().rbegin()->get()->timestamp(); + return messageEvents().rbegin()->get()->originTimestamp(); } int SpectralRoom::savedTopVisibleIndex() const { @@ -397,30 +397,30 @@ void SpectralRoom::changeAvatar(QUrl localFile) { const auto job = connection()->uploadFile(localFile.toLocalFile()); if (isJobRunning(job)) { connect(job, &BaseJob::success, this, [this, job] { - connection()->callApi( - id(), "m.room.avatar", QJsonObject{{"url", job->contentUri()}}); + connection()->callApi( + id(), "m.room.avatar", localUser()->id(), QJsonObject{{"url", job->contentUri()}}); }); } } void SpectralRoom::addLocalAlias(const QString& alias) { - auto aliases = localAliases(); - if (aliases.contains(alias)) + auto a = aliases(); + if (a.contains(alias)) return; - aliases += alias; + a += alias; - setLocalAliases(aliases); + setLocalAliases(a); } void SpectralRoom::removeLocalAlias(const QString& alias) { - auto aliases = localAliases(); - if (!aliases.contains(alias)) + auto a = aliases(); + if (!a.contains(alias)) return; - aliases.removeAll(alias); + a.removeAll(alias); - setLocalAliases(aliases); + setLocalAliases(a); } QString SpectralRoom::markdownToHTML(const QString& markdown) {