Merge branch 'master' into kirigami2

This commit is contained in:
Carl Schwan
2020-10-03 17:48:27 +02:00
19 changed files with 74 additions and 95 deletions

View File

@@ -1,10 +1,10 @@
image: Visual Studio 2017 image: Visual Studio 2019
environment: environment:
DEPLOY_DIR: Spectral-%APPVEYOR_BUILD_VERSION% DEPLOY_DIR: Spectral-%APPVEYOR_BUILD_VERSION%
matrix: matrix:
- QTDIR: C:\Qt\5.12\msvc2017_64 - QTDIR: C:\Qt\5.15\msvc2019_64
VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
PLATFORM: PLATFORM:
init: init:

View File

@@ -19,7 +19,7 @@ build-appimage:
- sudo cmake --build build --target install - sudo cmake --build build --target install
- cd .. - cd ..
- git clone https://github.com/commonmark/cmark.git && cd cmark - 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 . -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) - cmake --build build --target install --parallel $(nproc)
- cd .. - cd ..

View File

@@ -26,7 +26,7 @@ TextField {
states: [ states: [
State { State {
name: "shown" 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; scale: 0.8 }
PropertyChanges { target: floatingPlaceholder; anchors.topMargin: -floatingPlaceholder.height * 0.4 } PropertyChanges { target: floatingPlaceholder; anchors.topMargin: -floatingPlaceholder.height * 0.4 }
} }

View File

@@ -96,7 +96,7 @@ RowLayout {
} }
Label { Label {
readonly property int duration: content.info.duration || audio.duration || 0 readonly property int duration: content.info.duration ?? audio.duration ?? 0
Layout.fillWidth: true Layout.fillWidth: true
@@ -193,14 +193,12 @@ RowLayout {
} }
} }
function openSavedFile() function openSavedFile() {
{
if (Qt.openUrlExternally(progressInfo.localPath)) return; if (Qt.openUrlExternally(progressInfo.localPath)) return;
if (Qt.openUrlExternally(progressInfo.localDir)) return; if (Qt.openUrlExternally(progressInfo.localDir)) return;
} }
function humanSize(duration) function humanSize(duration) {
{
if (!duration) if (!duration)
return qsTr("Unknown", "Unknown duration") return qsTr("Unknown", "Unknown duration")
if (duration < 1000) if (duration < 1000)

View File

@@ -12,8 +12,14 @@ RowLayout {
default property alias innerObject : column.children default property alias innerObject : column.children
readonly property bool sentByMe: author.isLocalUser readonly property bool sentByMe: author.isLocalUser
readonly property bool replyVisible: reply || false readonly property bool darkBackground: !sentByMe
readonly property bool failed: marks === EventStatus.SendingFailed 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 id: root

View File

@@ -4,7 +4,7 @@ import QtQuick.Layouts 1.12
import Spectral.Setting 0.1 import Spectral.Setting 0.1
Flow { Flow {
visible: (reaction && reaction.length > 0) || false visible: (reaction && reaction.length > 0) ?? false
spacing: 8 spacing: 8

View File

@@ -120,7 +120,7 @@ RowLayout {
} }
Image { 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 readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
anchors.fill: parent anchors.fill: parent

View File

@@ -39,7 +39,7 @@ Dialog {
height: 48 height: 48
source: user.avatarMediaId source: user.avatarMediaId
hint: user.displayName || "No Name" hint: user.displayName ?? ""
Menu { Menu {
id: contextMenu id: contextMenu

View File

@@ -232,7 +232,7 @@ Dialog {
Layout.preferredWidth: 100 Layout.preferredWidth: 100
wrapMode: Label.Wrap wrapMode: Label.Wrap
text: "Main Alias" text: "Canonical Alias"
color: MPalette.lighter color: MPalette.lighter
} }
@@ -243,12 +243,12 @@ Dialog {
enabled: canChangeCanonicalAlias enabled: canChangeCanonicalAlias
model: room.remoteAliases model: room.aliases
currentIndex: room.remoteAliases.indexOf(room.canonicalAlias) currentIndex: room.aliases.indexOf(room.canonicalAlias)
onCurrentIndexChanged: { onCurrentIndexChanged: {
if (room.canonicalAlias != room.remoteAliases[currentIndex]) { if (room.canonicalAlias != room.aliases[currentIndex]) {
room.setCanonicalAlias(room.remoteAliases[currentIndex]) room.setCanonicalAlias(room.aliases[currentIndex])
} }
} }
} }
@@ -257,12 +257,14 @@ Dialog {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
visible: room.altAliases && room.altAliases.length
Label { Label {
Layout.preferredWidth: 100 Layout.preferredWidth: 100
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
wrapMode: Label.Wrap wrapMode: Label.Wrap
text: "Local Aliases" text: "Alt Aliases"
color: MPalette.lighter color: MPalette.lighter
} }
@@ -272,7 +274,7 @@ Dialog {
spacing: 0 spacing: 0
Repeater { Repeater {
model: room.localAliases model: room.altAliases
delegate: RowLayout { delegate: RowLayout {
Layout.maximumWidth: parent.width 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
}
}
}
}
} }
} }
} }

View File

@@ -135,7 +135,7 @@ Item {
glowRadius: modelData.blur/2 glowRadius: modelData.blur/2
spread: 0.05 spread: 0.05
color: _shadowColors[index] color: _shadowColors[index]
cornerRadius: modelData.blur + (effect.sourceItem.radius || 0) cornerRadius: modelData.blur + (effect.sourceItem.radius ?? 0)
} }
} }

View File

@@ -108,11 +108,6 @@ Kirigami.ScrollablePage {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
text: name || "No Name"
font.pixelSize: 16
font.bold: unreadCount >= 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
} }
Controls.Label { Controls.Label {

View File

@@ -130,9 +130,9 @@ Control {
keyNavigationWraps: true keyNavigationWraps: true
delegate: Control { delegate: Control {
property string autoCompleteText: modelData.displayName || modelData.unicode property string autoCompleteText: modelData.displayName ?? modelData.unicode
property bool isEmoji: modelData.unicode != null property bool isEmoji: modelData.unicode != null
readonly property bool highlighted: autoCompleteListView.currentIndex === index readonly property bool highlighted: autoCompleteListView.currentIndex == index
height: 36 height: 36
padding: 6 padding: 6
@@ -140,7 +140,7 @@ Control {
background: Rectangle { background: Rectangle {
visible: !isEmoji visible: !isEmoji
color: highlighted ? border.color : "transparent" color: highlighted ? border.color : "transparent"
border.color: isEmoji ? Material.accent : modelData.color border.color: isEmoji ? MPalette.accent : modelData.color
border.width: 2 border.width: 2
radius: height / 2 radius: height / 2
} }
@@ -164,7 +164,7 @@ Control {
Layout.preferredHeight: 24 Layout.preferredHeight: 24
visible: !isEmoji visible: !isEmoji
source: modelData.avatarMediaId || null source: modelData.avatarMediaId ?? null
color: modelData.color ? Qt.darker(modelData.color, 1.1) : MPalette.accent color: modelData.color ? Qt.darker(modelData.color, 1.1) : MPalette.accent
} }

View File

@@ -56,7 +56,7 @@ class MatrixImageProvider : public QObject, public QQuickAsyncImageProvider {
Quotient::Connection* connection() { return m_connection; } Quotient::Connection* connection() { return m_connection; }
void setConnection(Quotient::Connection* connection) { void setConnection(Quotient::Connection* connection) {
m_connection.store(connection); m_connection.storeRelaxed(connection);
emit connectionChanged(); emit connectionChanged();
} }

View File

@@ -203,13 +203,13 @@ int MessageEventModel::refreshEventRoles(const QString& id,
} }
inline bool hasValidTimestamp(const Quotient::TimelineItem& ti) { inline bool hasValidTimestamp(const Quotient::TimelineItem& ti) {
return ti->timestamp().isValid(); return ti->originTimestamp().isValid();
} }
QDateTime MessageEventModel::makeMessageTimestamp( QDateTime MessageEventModel::makeMessageTimestamp(
const Quotient::Room::rev_iter_t& baseIt) const { const Quotient::Room::rev_iter_t& baseIt) const {
const auto& timeline = m_currentRoom->messageEvents(); const auto& timeline = m_currentRoom->messageEvents();
auto ts = baseIt->event()->timestamp(); auto ts = baseIt->event()->originTimestamp();
if (ts.isValid()) if (ts.isValid())
return ts; return ts;
@@ -218,10 +218,10 @@ QDateTime MessageEventModel::makeMessageTimestamp(
using Quotient::TimelineItem; using Quotient::TimelineItem;
auto rit = std::find_if(baseIt, timeline.rend(), hasValidTimestamp); auto rit = std::find_if(baseIt, timeline.rend(), hasValidTimestamp);
if (rit != timeline.rend()) 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); auto it = std::find_if(baseIt.base(), timeline.end(), hasValidTimestamp);
if (it != timeline.end()) 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?.. // What kind of room is that?..
qCritical() << "No valid timestamps in the room timeline!"; qCritical() << "No valid timestamps in the room timeline!";
@@ -238,7 +238,8 @@ QString MessageEventModel::renderDate(QDateTime timestamp) const {
return tr("The day before yesterday"); return tr("The day before yesterday");
if (date > QDate::currentDate().addDays(-7)) if (date > QDate::currentDate().addDays(-7))
return date.toString("dddd"); return date.toString("dddd");
return date.toString(Qt::DefaultLocaleShortDate);
return QLocale::system().toString(date, QLocale::ShortFormat);
} }
void MessageEventModel::refreshLastUserEvents(int baseTimelineRow) { void MessageEventModel::refreshLastUserEvents(int baseTimelineRow) {

View File

@@ -6,7 +6,7 @@
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <QtDBus/QDBusArgument> #include <QtDBus/QDBusArgument>
#include <QtDBus/QDBusInterface> #include <QtDBus/QDBusInterface>
#endif #endif
@@ -25,8 +25,9 @@ class NotificationsManager : public QObject {
void notificationClicked(const QString roomId, const QString eventId); void notificationClicked(const QString roomId, const QString eventId);
private: 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; QDBusInterface dbus;
bool serverSupportsHtml = false;
uint showNotification(const QString summary, uint showNotification(const QString summary,
const QString text, const QString text,
const QImage image); const QImage image);
@@ -49,7 +50,7 @@ class NotificationsManager : public QObject {
const QImage& icon); 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); QDBusArgument& operator<<(QDBusArgument& arg, const QImage& image);
const QDBusArgument& operator>>(const QDBusArgument& arg, QImage&); const QDBusArgument& operator>>(const QDBusArgument& arg, QImage&);
#endif #endif

View File

@@ -5,6 +5,7 @@
#include <QtDBus/QDBusConnection> #include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusMessage> #include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusMetaType> #include <QtDBus/QDBusMetaType>
#include <QtDBus/QDBusReply>
NotificationsManager::NotificationsManager(QObject *parent) NotificationsManager::NotificationsManager(QObject *parent)
: QObject(parent), : QObject(parent),
@@ -13,6 +14,15 @@ NotificationsManager::NotificationsManager(QObject *parent)
this) { this) {
qDBusRegisterMetaType<QImage>(); qDBusRegisterMetaType<QImage>();
const QDBusReply<QStringList> 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( QDBusConnection::sessionBus().connect(
"org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "/org/freedesktop/Notifications",
"org.freedesktop.Notifications", "ActionInvoked", this, "org.freedesktop.Notifications", "ActionInvoked", this,
@@ -54,14 +64,17 @@ uint NotificationsManager::showNotification(const QString summary,
croppedImage = image; croppedImage = image;
} }
const QString body = serverSupportsHtml ? text.toHtmlEscaped() : text;
QVariantMap hints; QVariantMap hints;
hints["image-data"] = croppedImage; hints["image-data"] = croppedImage;
hints["desktop-entry"] = "org.eu.encom.spectral";
QList<QVariant> argumentList; QList<QVariant> argumentList;
argumentList << "Spectral"; // app_name argumentList << "Spectral"; // app_name
argumentList << uint(0); // replace_id argumentList << uint(0); // replace_id
argumentList << ""; // app_icon argumentList << ""; // app_icon
argumentList << summary; // summary argumentList << summary; // summary
argumentList << text; // body argumentList << body; // body
argumentList << (QStringList("default") << "reply"); // actions argumentList << (QStringList("default") << "reply"); // actions
argumentList << hints; // hints argumentList << hints; // hints
argumentList << int(-1); // timeout in ms argumentList << int(-1); // timeout in ms

View File

@@ -110,15 +110,14 @@ void PublicRoomListModel::next(int count) {
attempted = true; attempted = true;
if (job->status() == BaseJob::Success) { if (job->status() == BaseJob::Success) {
auto resp = job->data(); nextBatch = job->nextBatch();
nextBatch = resp.nextBatch;
this->beginInsertRows({}, rooms.count(), this->beginInsertRows({}, rooms.count(),
rooms.count() + resp.chunk.count() - 1); rooms.count() + job->chunk().count() - 1);
rooms.append(resp.chunk); rooms.append(job->chunk());
this->endInsertRows(); this->endInsertRows();
if (resp.nextBatch.isEmpty()) { if (job->nextBatch().isEmpty()) {
emit hasMoreChanged(); emit hasMoreChanged();
} }
} }

View File

@@ -167,7 +167,7 @@ void SpectralRoom::countChanged() {
QDateTime SpectralRoom::lastActiveTime() const { QDateTime SpectralRoom::lastActiveTime() const {
if (timelineSize() == 0) if (timelineSize() == 0)
return QDateTime(); return QDateTime();
return messageEvents().rbegin()->get()->timestamp(); return messageEvents().rbegin()->get()->originTimestamp();
} }
int SpectralRoom::savedTopVisibleIndex() const { int SpectralRoom::savedTopVisibleIndex() const {
@@ -397,30 +397,30 @@ void SpectralRoom::changeAvatar(QUrl localFile) {
const auto job = connection()->uploadFile(localFile.toLocalFile()); const auto job = connection()->uploadFile(localFile.toLocalFile());
if (isJobRunning(job)) { if (isJobRunning(job)) {
connect(job, &BaseJob::success, this, [this, job] { connect(job, &BaseJob::success, this, [this, job] {
connection()->callApi<SetRoomStateJob>( connection()->callApi<SetRoomStateWithKeyJob>(
id(), "m.room.avatar", QJsonObject{{"url", job->contentUri()}}); id(), "m.room.avatar", localUser()->id(), QJsonObject{{"url", job->contentUri()}});
}); });
} }
} }
void SpectralRoom::addLocalAlias(const QString& alias) { void SpectralRoom::addLocalAlias(const QString& alias) {
auto aliases = localAliases(); auto a = aliases();
if (aliases.contains(alias)) if (a.contains(alias))
return; return;
aliases += alias; a += alias;
setLocalAliases(aliases); setLocalAliases(a);
} }
void SpectralRoom::removeLocalAlias(const QString& alias) { void SpectralRoom::removeLocalAlias(const QString& alias) {
auto aliases = localAliases(); auto a = aliases();
if (!aliases.contains(alias)) if (!a.contains(alias))
return; return;
aliases.removeAll(alias); a.removeAll(alias);
setLocalAliases(aliases); setLocalAliases(a);
} }
QString SpectralRoom::markdownToHTML(const QString& markdown) { QString SpectralRoom::markdownToHTML(const QString& markdown) {