mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-21 12:35:52 +03:00
Add an incoming talk call notification to the desktop client
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com> Co-authored-by: Camila <hello@camila.codes>
This commit is contained in:
parent
4459b92f80
commit
3f5243aaee
21 changed files with 555 additions and 34 deletions
|
@ -29,5 +29,6 @@
|
||||||
<file>src/gui/tray/ActivityItemActions.qml</file>
|
<file>src/gui/tray/ActivityItemActions.qml</file>
|
||||||
<file>src/gui/tray/ActivityItemContent.qml</file>
|
<file>src/gui/tray/ActivityItemContent.qml</file>
|
||||||
<file>src/gui/tray/TalkReplyTextField.qml</file>
|
<file>src/gui/tray/TalkReplyTextField.qml</file>
|
||||||
|
<file>src/gui/tray/CallNotificationDialog.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -146,6 +146,10 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
||||||
this, &GeneralSettings::slotToggleOptionalServerNotifications);
|
this, &GeneralSettings::slotToggleOptionalServerNotifications);
|
||||||
_ui->serverNotificationsCheckBox->setToolTip(tr("Server notifications that require attention."));
|
_ui->serverNotificationsCheckBox->setToolTip(tr("Server notifications that require attention."));
|
||||||
|
|
||||||
|
connect(_ui->callNotificationsCheckBox, &QAbstractButton::toggled,
|
||||||
|
this, &GeneralSettings::slotToggleCallNotifications);
|
||||||
|
_ui->callNotificationsCheckBox->setToolTip(tr("Show call notification dialogs."));
|
||||||
|
|
||||||
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
|
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
|
||||||
|
|
||||||
// Rename 'Explorer' appropriately on non-Windows
|
// Rename 'Explorer' appropriately on non-Windows
|
||||||
|
@ -247,6 +251,8 @@ void GeneralSettings::loadMiscSettings()
|
||||||
ConfigFile cfgFile;
|
ConfigFile cfgFile;
|
||||||
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
|
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
|
||||||
_ui->serverNotificationsCheckBox->setChecked(cfgFile.optionalServerNotifications());
|
_ui->serverNotificationsCheckBox->setChecked(cfgFile.optionalServerNotifications());
|
||||||
|
_ui->callNotificationsCheckBox->setEnabled(_ui->serverNotificationsCheckBox->isEnabled());
|
||||||
|
_ui->callNotificationsCheckBox->setChecked(cfgFile.showCallNotifications());
|
||||||
_ui->showInExplorerNavigationPaneCheckBox->setChecked(cfgFile.showInExplorerNavigationPane());
|
_ui->showInExplorerNavigationPaneCheckBox->setChecked(cfgFile.showInExplorerNavigationPane());
|
||||||
_ui->crashreporterCheckBox->setChecked(cfgFile.crashReporter());
|
_ui->crashreporterCheckBox->setChecked(cfgFile.crashReporter());
|
||||||
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
|
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
|
||||||
|
@ -428,6 +434,13 @@ void GeneralSettings::slotToggleOptionalServerNotifications(bool enable)
|
||||||
{
|
{
|
||||||
ConfigFile cfgFile;
|
ConfigFile cfgFile;
|
||||||
cfgFile.setOptionalServerNotifications(enable);
|
cfgFile.setOptionalServerNotifications(enable);
|
||||||
|
_ui->callNotificationsCheckBox->setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::slotToggleCallNotifications(bool enable)
|
||||||
|
{
|
||||||
|
ConfigFile cfgFile;
|
||||||
|
cfgFile.setShowCallNotifications(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::slotShowInExplorerNavigationPane(bool checked)
|
void GeneralSettings::slotShowInExplorerNavigationPane(bool checked)
|
||||||
|
|
|
@ -48,6 +48,7 @@ private slots:
|
||||||
void saveMiscSettings();
|
void saveMiscSettings();
|
||||||
void slotToggleLaunchOnStartup(bool);
|
void slotToggleLaunchOnStartup(bool);
|
||||||
void slotToggleOptionalServerNotifications(bool);
|
void slotToggleOptionalServerNotifications(bool);
|
||||||
|
void slotToggleCallNotifications(bool);
|
||||||
void slotShowInExplorerNavigationPane(bool);
|
void slotShowInExplorerNavigationPane(bool);
|
||||||
void slotIgnoreFilesEditor();
|
void slotIgnoreFilesEditor();
|
||||||
void slotCreateDebugArchive();
|
void slotCreateDebugArchive();
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>554</width>
|
<width>556</width>
|
||||||
<height>558</height>
|
<height>563</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -90,6 +90,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="callNotificationsCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show Call Notifications</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
#include <QVariantMap>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
@ -159,6 +160,44 @@ void Systray::create()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Systray::createCallDialog(const Activity &callNotification)
|
||||||
|
{
|
||||||
|
qCDebug(lcSystray) << "Starting a new call dialog for notification with id: " << callNotification._id << "with text: " << callNotification._subject;
|
||||||
|
|
||||||
|
if (_trayEngine && !_callsAlreadyNotified.contains(callNotification._id)) {
|
||||||
|
const QVariantMap talkNotificationData{
|
||||||
|
{"conversationToken", callNotification._talkNotificationData.conversationToken},
|
||||||
|
{"messageId", callNotification._talkNotificationData.messageId},
|
||||||
|
{"messageSent", callNotification._talkNotificationData.messageSent},
|
||||||
|
{"userAvatar", callNotification._talkNotificationData.userAvatar},
|
||||||
|
};
|
||||||
|
|
||||||
|
QVariantList links;
|
||||||
|
for(const auto &link : callNotification._links) {
|
||||||
|
links.append(QVariantMap{
|
||||||
|
{"imageSource", link._imageSource},
|
||||||
|
{"imageSourceHovered", link._imageSourceHovered},
|
||||||
|
{"label", link._label},
|
||||||
|
{"link", link._link},
|
||||||
|
{"primary", link._primary},
|
||||||
|
{"verb", link._verb},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVariantMap initialProperties{
|
||||||
|
{"talkNotificationData", talkNotificationData},
|
||||||
|
{"links", links},
|
||||||
|
{"subject", callNotification._subject},
|
||||||
|
{"link", callNotification._link},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto callDialog = new QQmlComponent(_trayEngine, QStringLiteral("qrc:/qml/src/gui/tray/CallNotificationDialog.qml"));
|
||||||
|
callDialog->createWithInitialProperties(initialProperties);
|
||||||
|
|
||||||
|
_callsAlreadyNotified.insert(callNotification._id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Systray::slotNewUserSelected()
|
void Systray::slotNewUserSelected()
|
||||||
{
|
{
|
||||||
if (_trayEngine) {
|
if (_trayEngine) {
|
||||||
|
@ -308,6 +347,30 @@ void Systray::forceWindowInit(QQuickWindow *window) const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Systray::positionNotificationWindow(QQuickWindow *window) const
|
||||||
|
{
|
||||||
|
if (!useNormalWindow()) {
|
||||||
|
window->setScreen(currentScreen());
|
||||||
|
if(geometry().isValid()) {
|
||||||
|
// On OSes where the QSystemTrayIcon geometry method isn't borked, we can actually figure out where the system tray is located
|
||||||
|
// We can therefore use our normal routines
|
||||||
|
const auto position = computeNotificationPosition(window->width(), window->height());
|
||||||
|
window->setPosition(position);
|
||||||
|
} else if (QProcessEnvironment::systemEnvironment().contains(QStringLiteral("XDG_CURRENT_DESKTOP")) &&
|
||||||
|
(QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_CURRENT_DESKTOP")).contains(QStringLiteral("GNOME")))) {
|
||||||
|
// We can safely hardcode the top-right position for the notification when running GNOME
|
||||||
|
const auto position = computeNotificationPosition(window->width(), window->height(), 0, NotificationPosition::TopRight);
|
||||||
|
window->setPosition(position);
|
||||||
|
} else {
|
||||||
|
// For other DEs we play it safe and place the notification in the centre of the screen
|
||||||
|
const QPoint windowAdjustment(window->geometry().width() / 2, window->geometry().height() / 2);
|
||||||
|
const auto position = currentScreen()->geometry().center();// - windowAdjustment;
|
||||||
|
window->setPosition(position);
|
||||||
|
}
|
||||||
|
// TODO: Get actual notification positions for the DEs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QScreen *Systray::currentScreen() const
|
QScreen *Systray::currentScreen() const
|
||||||
{
|
{
|
||||||
const auto screen = QGuiApplication::screenAt(QCursor::pos());
|
const auto screen = QGuiApplication::screenAt(QCursor::pos());
|
||||||
|
@ -446,8 +509,85 @@ QPoint Systray::computeWindowReferencePoint() const
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPoint Systray::computeNotificationReferencePoint(int spacing, NotificationPosition position) const
|
||||||
|
{
|
||||||
|
auto trayIconCenter = calcTrayIconCenter();
|
||||||
|
auto taskbarScreenEdge = taskbarOrientation();
|
||||||
|
auto taskbarRect = taskbarGeometry();
|
||||||
|
const auto screenRect = currentScreenRect();
|
||||||
|
|
||||||
|
if(position == NotificationPosition::TopLeft) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Top;
|
||||||
|
trayIconCenter = QPoint(0, 0);
|
||||||
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
||||||
|
} else if(position == NotificationPosition::TopRight) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Top;
|
||||||
|
trayIconCenter = QPoint(screenRect.width(), 0);
|
||||||
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
||||||
|
} else if(position == NotificationPosition::BottomLeft) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
||||||
|
trayIconCenter = QPoint(0, screenRect.height());
|
||||||
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
||||||
|
} else if(position == NotificationPosition::BottomRight) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
||||||
|
trayIconCenter = QPoint(screenRect.width(), screenRect.height());
|
||||||
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
||||||
|
qCDebug(lcSystray) << "taskbarRect:" << taskbarRect;
|
||||||
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
||||||
|
qCDebug(lcSystray) << "trayIconCenter:" << trayIconCenter;
|
||||||
|
|
||||||
|
switch(taskbarScreenEdge) {
|
||||||
|
case TaskBarPosition::Bottom:
|
||||||
|
return {
|
||||||
|
trayIconCenter.x() < screenRect.center().x() ? screenRect.left() + spacing : screenRect.right() - spacing,
|
||||||
|
screenRect.bottom() - taskbarRect.height() - spacing
|
||||||
|
};
|
||||||
|
case TaskBarPosition::Left:
|
||||||
|
return {
|
||||||
|
screenRect.left() + taskbarRect.width() + spacing,
|
||||||
|
trayIconCenter.y() < screenRect.center().y() ? screenRect.top() + spacing : screenRect.bottom() - spacing
|
||||||
|
};
|
||||||
|
case TaskBarPosition::Top:
|
||||||
|
return {
|
||||||
|
trayIconCenter.x() < screenRect.center().x() ? screenRect.left() + spacing : screenRect.right() - spacing,
|
||||||
|
screenRect.top() + taskbarRect.height() + spacing
|
||||||
|
};
|
||||||
|
case TaskBarPosition::Right:
|
||||||
|
return {
|
||||||
|
screenRect.right() - taskbarRect.width() - spacing,
|
||||||
|
trayIconCenter.y() < screenRect.center().y() ? screenRect.top() + spacing : screenRect.bottom() - spacing
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Systray::computeWindowRect(int spacing, const QPoint &topLeft, const QPoint &bottomRight) const
|
||||||
|
{
|
||||||
|
const auto screenRect = currentScreenRect();
|
||||||
|
const auto rect = QRect(topLeft, bottomRight);
|
||||||
|
auto offset = QPoint();
|
||||||
|
|
||||||
|
if (rect.left() < screenRect.left()) {
|
||||||
|
offset.setX(screenRect.left() - rect.left() + spacing);
|
||||||
|
} else if (rect.right() > screenRect.right()) {
|
||||||
|
offset.setX(screenRect.right() - rect.right() - spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rect.top() < screenRect.top()) {
|
||||||
|
offset.setY(screenRect.top() - rect.top() + spacing);
|
||||||
|
} else if (rect.bottom() > screenRect.bottom()) {
|
||||||
|
offset.setY(screenRect.bottom() - rect.bottom() - spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect.translated(offset);
|
||||||
|
}
|
||||||
|
|
||||||
QPoint Systray::computeWindowPosition(int width, int height) const
|
QPoint Systray::computeWindowPosition(int width, int height) const
|
||||||
{
|
{
|
||||||
|
constexpr auto spacing = 4;
|
||||||
const auto referencePoint = computeWindowReferencePoint();
|
const auto referencePoint = computeWindowReferencePoint();
|
||||||
|
|
||||||
const auto taskbarScreenEdge = taskbarOrientation();
|
const auto taskbarScreenEdge = taskbarOrientation();
|
||||||
|
@ -467,24 +607,7 @@ QPoint Systray::computeWindowPosition(int width, int height) const
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}();
|
}();
|
||||||
const auto bottomRight = topLeft + QPoint(width, height);
|
const auto bottomRight = topLeft + QPoint(width, height);
|
||||||
const auto windowRect = [=]() {
|
const auto windowRect = computeWindowRect(spacing, topLeft, bottomRight);
|
||||||
const auto rect = QRect(topLeft, bottomRight);
|
|
||||||
auto offset = QPoint();
|
|
||||||
|
|
||||||
if (rect.left() < screenRect.left()) {
|
|
||||||
offset.setX(screenRect.left() - rect.left() + 4);
|
|
||||||
} else if (rect.right() > screenRect.right()) {
|
|
||||||
offset.setX(screenRect.right() - rect.right() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rect.top() < screenRect.top()) {
|
|
||||||
offset.setY(screenRect.top() - rect.top() + 4);
|
|
||||||
} else if (rect.bottom() > screenRect.bottom()) {
|
|
||||||
offset.setY(screenRect.bottom() - rect.bottom() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rect.translated(offset);
|
|
||||||
}();
|
|
||||||
|
|
||||||
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
||||||
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
||||||
|
@ -494,17 +617,64 @@ QPoint Systray::computeWindowPosition(int width, int height) const
|
||||||
return windowRect.topLeft();
|
return windowRect.topLeft();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPoint Systray::computeNotificationPosition(int width, int height, int spacing, NotificationPosition position) const
|
||||||
|
{
|
||||||
|
const auto referencePoint = computeNotificationReferencePoint(spacing, position);
|
||||||
|
|
||||||
|
auto trayIconCenter = calcTrayIconCenter();
|
||||||
|
auto taskbarScreenEdge = taskbarOrientation();
|
||||||
|
const auto screenRect = currentScreenRect();
|
||||||
|
|
||||||
|
if(position == NotificationPosition::TopLeft) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Top;
|
||||||
|
trayIconCenter = QPoint(0, 0);
|
||||||
|
} else if(position == NotificationPosition::TopRight) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Top;
|
||||||
|
trayIconCenter = QPoint(screenRect.width(), 0);
|
||||||
|
} else if(position == NotificationPosition::BottomLeft) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
||||||
|
trayIconCenter = QPoint(0, screenRect.height());
|
||||||
|
} else if(position == NotificationPosition::BottomRight) {
|
||||||
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
||||||
|
trayIconCenter = QPoint(screenRect.width(), screenRect.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto topLeft = [=]() {
|
||||||
|
switch(taskbarScreenEdge) {
|
||||||
|
case TaskBarPosition::Bottom:
|
||||||
|
return trayIconCenter.x() < screenRect.center().x() ? referencePoint - QPoint(0, height) : referencePoint - QPoint(width, height);
|
||||||
|
case TaskBarPosition::Left:
|
||||||
|
return trayIconCenter.y() < screenRect.center().y() ? referencePoint : referencePoint - QPoint(0, height);
|
||||||
|
case TaskBarPosition::Top:
|
||||||
|
return trayIconCenter.x() < screenRect.center().x() ? referencePoint : referencePoint - QPoint(width, 0);
|
||||||
|
case TaskBarPosition::Right:
|
||||||
|
return trayIconCenter.y() < screenRect.center().y() ? referencePoint - QPoint(width, 0) : QPoint(width, height);
|
||||||
|
}
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}();
|
||||||
|
const auto bottomRight = topLeft + QPoint(width, height);
|
||||||
|
const auto windowRect = computeWindowRect(spacing, topLeft, bottomRight);
|
||||||
|
|
||||||
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
||||||
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
||||||
|
qCDebug(lcSystray) << "windowRect (reference)" << QRect(topLeft, bottomRight);
|
||||||
|
qCDebug(lcSystray) << "windowRect (adjusted)" << windowRect;
|
||||||
|
qCDebug(lcSystray) << "referencePoint" << referencePoint;
|
||||||
|
|
||||||
|
return windowRect.topLeft();
|
||||||
|
}
|
||||||
|
|
||||||
QPoint Systray::calcTrayIconCenter() const
|
QPoint Systray::calcTrayIconCenter() const
|
||||||
{
|
{
|
||||||
// QSystemTrayIcon::geometry() is broken for ages on most Linux DEs (invalid geometry returned)
|
if(geometry().isValid()) {
|
||||||
// thus we can use this only for Windows and macOS
|
// QSystemTrayIcon::geometry() is broken for ages on most Linux DEs (invalid geometry returned)
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
// thus we can use this only for Windows and macOS
|
||||||
auto trayIconCenter = geometry().center();
|
auto trayIconCenter = geometry().center();
|
||||||
return trayIconCenter;
|
return trayIconCenter;
|
||||||
#else
|
}
|
||||||
|
|
||||||
// On Linux, fall back to mouse position (assuming tray icon is activated by mouse click)
|
// On Linux, fall back to mouse position (assuming tray icon is activated by mouse click)
|
||||||
return QCursor::pos(currentScreen());
|
return QCursor::pos(currentScreen());
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessManagerFactory::AccessManagerFactory()
|
AccessManagerFactory::AccessManagerFactory()
|
||||||
|
|
|
@ -64,6 +64,9 @@ public:
|
||||||
|
|
||||||
enum class TaskBarPosition { Bottom, Left, Top, Right };
|
enum class TaskBarPosition { Bottom, Left, Top, Right };
|
||||||
Q_ENUM(TaskBarPosition);
|
Q_ENUM(TaskBarPosition);
|
||||||
|
|
||||||
|
enum class NotificationPosition { Default, TopLeft, TopRight, BottomLeft, BottomRight };
|
||||||
|
Q_ENUM(NotificationPosition);
|
||||||
|
|
||||||
void setTrayEngine(QQmlApplicationEngine *trayEngine);
|
void setTrayEngine(QQmlApplicationEngine *trayEngine);
|
||||||
void create();
|
void create();
|
||||||
|
@ -72,6 +75,7 @@ public:
|
||||||
bool isOpen();
|
bool isOpen();
|
||||||
QString windowTitle() const;
|
QString windowTitle() const;
|
||||||
bool useNormalWindow() const;
|
bool useNormalWindow() const;
|
||||||
|
void createCallDialog(const Activity &callNotification);
|
||||||
|
|
||||||
Q_INVOKABLE void pauseResumeSync();
|
Q_INVOKABLE void pauseResumeSync();
|
||||||
Q_INVOKABLE bool syncIsPaused();
|
Q_INVOKABLE bool syncIsPaused();
|
||||||
|
@ -79,6 +83,7 @@ public:
|
||||||
Q_INVOKABLE void setClosed();
|
Q_INVOKABLE void setClosed();
|
||||||
Q_INVOKABLE void positionWindow(QQuickWindow *window) const;
|
Q_INVOKABLE void positionWindow(QQuickWindow *window) const;
|
||||||
Q_INVOKABLE void forceWindowInit(QQuickWindow *window) const;
|
Q_INVOKABLE void forceWindowInit(QQuickWindow *window) const;
|
||||||
|
Q_INVOKABLE void positionNotificationWindow(QQuickWindow *window) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void currentUserChanged();
|
void currentUserChanged();
|
||||||
|
@ -110,16 +115,21 @@ private:
|
||||||
QScreen *currentScreen() const;
|
QScreen *currentScreen() const;
|
||||||
QRect currentScreenRect() const;
|
QRect currentScreenRect() const;
|
||||||
QPoint computeWindowReferencePoint() const;
|
QPoint computeWindowReferencePoint() const;
|
||||||
|
QPoint computeNotificationReferencePoint(int spacing = 20, NotificationPosition position = NotificationPosition::Default) const;
|
||||||
QPoint calcTrayIconCenter() const;
|
QPoint calcTrayIconCenter() const;
|
||||||
TaskBarPosition taskbarOrientation() const;
|
TaskBarPosition taskbarOrientation() const;
|
||||||
QRect taskbarGeometry() const;
|
QRect taskbarGeometry() const;
|
||||||
|
QRect computeWindowRect(int spacing, const QPoint &topLeft, const QPoint &bottomRight) const;
|
||||||
QPoint computeWindowPosition(int width, int height) const;
|
QPoint computeWindowPosition(int width, int height) const;
|
||||||
|
QPoint computeNotificationPosition(int width, int height, int spacing = 20, NotificationPosition position = NotificationPosition::Default) const;
|
||||||
|
|
||||||
bool _isOpen = false;
|
bool _isOpen = false;
|
||||||
bool _syncIsPaused = true;
|
bool _syncIsPaused = true;
|
||||||
QPointer<QQmlApplicationEngine> _trayEngine;
|
QPointer<QQmlApplicationEngine> _trayEngine;
|
||||||
|
|
||||||
AccessManagerFactory _accessManagerFactory;
|
AccessManagerFactory _accessManagerFactory;
|
||||||
|
|
||||||
|
QSet<qlonglong> _callsAlreadyNotified;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -15,6 +15,7 @@ MouseArea {
|
||||||
readonly property bool isChatActivity: model.objectType === "chat" || model.objectType === "room" || model.objectType === "call"
|
readonly property bool isChatActivity: model.objectType === "chat" || model.objectType === "room" || model.objectType === "call"
|
||||||
readonly property bool isTalkReplyPossible: model.conversationToken !== ""
|
readonly property bool isTalkReplyPossible: model.conversationToken !== ""
|
||||||
property bool isTalkReplyOptionVisible: model.messageSent !== ""
|
property bool isTalkReplyOptionVisible: model.messageSent !== ""
|
||||||
|
readonly property bool isCallActivity: model.objectType === "call"
|
||||||
|
|
||||||
signal fileActivityButtonClicked(string absolutePath)
|
signal fileActivityButtonClicked(string absolutePath)
|
||||||
|
|
||||||
|
|
244
src/gui/tray/CallNotificationDialog.qml
Normal file
244
src/gui/tray/CallNotificationDialog.qml
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Window 2.15
|
||||||
|
import Style 1.0
|
||||||
|
import com.nextcloud.desktopclient 1.0
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
import QtMultimedia 5.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtGraphicalEffects 1.15
|
||||||
|
|
||||||
|
Window {
|
||||||
|
id: root
|
||||||
|
color: "transparent"
|
||||||
|
flags: Qt.Dialog | Qt.FramelessWindowHint
|
||||||
|
|
||||||
|
readonly property int windowSpacing: 10
|
||||||
|
readonly property int windowWidth: 240
|
||||||
|
|
||||||
|
readonly property string svgImage: "image://svgimage-custom-color/%1.svg" + "/"
|
||||||
|
readonly property string talkIcon: svgImage.arg("wizard-talk")
|
||||||
|
readonly property string deleteIcon: svgImage.arg("delete")
|
||||||
|
|
||||||
|
// We set talkNotificationData, subject, and links properties in C++
|
||||||
|
property var talkNotificationData: ({})
|
||||||
|
property string subject: ""
|
||||||
|
property var links: []
|
||||||
|
property string link: ""
|
||||||
|
property string ringtonePath: "qrc:///client/theme/call-notification.wav"
|
||||||
|
|
||||||
|
readonly property bool usingUserAvatar: root.talkNotificationData.userAvatar !== ""
|
||||||
|
|
||||||
|
function closeNotification() {
|
||||||
|
ringSound.stop();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
width: root.windowWidth
|
||||||
|
height: rootBackground.height
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Systray.forceWindowInit(root);
|
||||||
|
Systray.positionNotificationWindow(root);
|
||||||
|
|
||||||
|
root.show();
|
||||||
|
root.raise();
|
||||||
|
root.requestActivate();
|
||||||
|
|
||||||
|
ringSound.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio {
|
||||||
|
id: ringSound
|
||||||
|
source: root.ringtonePath
|
||||||
|
loops: 9 // about 45 seconds of audio playing
|
||||||
|
audioRole: Audio.RingtoneRole
|
||||||
|
onStopped: root.closeNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rootBackground
|
||||||
|
width: parent.width
|
||||||
|
height: contentLayout.height + (root.windowSpacing * 2)
|
||||||
|
radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius
|
||||||
|
color: Style.backgroundColor
|
||||||
|
border.width: Style.trayWindowBorderWidth
|
||||||
|
border.color: Style.menuBorder
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: backgroundLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.usingUserAvatar
|
||||||
|
sourceComponent: Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: backgroundImage
|
||||||
|
anchors.fill: parent
|
||||||
|
cache: true
|
||||||
|
source: root.talkNotificationData.userAvatar
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
smooth: true
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
FastBlur {
|
||||||
|
id: backgroundBlur
|
||||||
|
anchors.fill: backgroundImage
|
||||||
|
source: backgroundImage
|
||||||
|
radius: 50
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: backgroundMask
|
||||||
|
color: "white"
|
||||||
|
radius: rootBackground.radius
|
||||||
|
anchors.fill: backgroundImage
|
||||||
|
visible: false
|
||||||
|
width: backgroundImage.paintedWidth
|
||||||
|
height: backgroundImage.paintedHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
OpacityMask {
|
||||||
|
id: backgroundOpacityMask
|
||||||
|
anchors.fill: backgroundBlur
|
||||||
|
source: backgroundBlur
|
||||||
|
maskSource: backgroundMask
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: darkenerRect
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.4
|
||||||
|
visible: backgroundOpacityMask.visible
|
||||||
|
radius: rootBackground.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentLayout
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: root.windowSpacing
|
||||||
|
spacing: root.windowSpacing
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: Style.accountAvatarSize
|
||||||
|
height: Style.accountAvatarSize
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: callerAvatar
|
||||||
|
anchors.fill: parent
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
source: root.usingUserAvatar ? root.talkNotificationData.userAvatar :
|
||||||
|
Theme.darkMode ? root.talkIcon + Style.ncTextColor : root.talkIcon + Style.ncBlue
|
||||||
|
sourceSize.width: Style.accountAvatarSize
|
||||||
|
sourceSize.height: Style.accountAvatarSize
|
||||||
|
|
||||||
|
visible: !root.usingUserAvatar
|
||||||
|
|
||||||
|
Accessible.role: Accessible.Indicator
|
||||||
|
Accessible.name: qsTr("Talk notification caller avatar")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask
|
||||||
|
color: "white"
|
||||||
|
radius: width * 0.5
|
||||||
|
anchors.fill: callerAvatar
|
||||||
|
visible: false
|
||||||
|
width: callerAvatar.paintedWidth
|
||||||
|
height: callerAvatar.paintedHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
OpacityMask {
|
||||||
|
anchors.fill: callerAvatar
|
||||||
|
source: callerAvatar
|
||||||
|
maskSource: mask
|
||||||
|
visible: root.usingUserAvatar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: message
|
||||||
|
text: root.subject
|
||||||
|
color: root.usingUserAvatar ? "white" : Style.ncTextColor
|
||||||
|
font.pixelSize: Style.topLinePixelSize
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: root.windowSpacing / 2
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: linksRepeater
|
||||||
|
model: root.links
|
||||||
|
|
||||||
|
CustomButton {
|
||||||
|
id: answerCall
|
||||||
|
readonly property string verb: modelData.verb
|
||||||
|
readonly property bool isAnswerCallButton: verb === "WEB"
|
||||||
|
|
||||||
|
visible: isAnswerCallButton
|
||||||
|
text: modelData.label
|
||||||
|
bold: true
|
||||||
|
bgColor: Style.ncBlue
|
||||||
|
bgOpacity: 0.8
|
||||||
|
|
||||||
|
textColor: Style.ncHeaderTextColor
|
||||||
|
|
||||||
|
imageSource: root.talkIcon + Style.ncHeaderTextColor
|
||||||
|
imageSourceHover: root.talkIcon + Style.ncHeaderTextColor
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Style.callNotificationPrimaryButtonMinHeight
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Qt.openUrlExternally(root.link);
|
||||||
|
root.closeNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
Accessible.role: Accessible.Button
|
||||||
|
Accessible.name: qsTr("Answer Talk call notification")
|
||||||
|
Accessible.onPressAction: answerCall.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomButton {
|
||||||
|
id: declineCall
|
||||||
|
text: qsTr("Decline")
|
||||||
|
bold: true
|
||||||
|
bgColor: Style.errorBoxBackgroundColor
|
||||||
|
bgOpacity: 0.8
|
||||||
|
|
||||||
|
textColor: Style.ncHeaderTextColor
|
||||||
|
|
||||||
|
imageSource: root.deleteIcon + "white"
|
||||||
|
imageSourceHover: root.deleteIcon + "white"
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Style.callNotificationPrimaryButtonMinHeight
|
||||||
|
|
||||||
|
onClicked: root.closeNotification()
|
||||||
|
|
||||||
|
Accessible.role: Accessible.Button
|
||||||
|
Accessible.name: qsTr("Decline Talk call notification")
|
||||||
|
Accessible.onPressAction: declineCall.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,19 +8,22 @@ Button {
|
||||||
|
|
||||||
property string imageSource: ""
|
property string imageSource: ""
|
||||||
property string imageSourceHover: ""
|
property string imageSourceHover: ""
|
||||||
|
property Image iconItem: icon
|
||||||
|
|
||||||
property string toolTipText: ""
|
property string toolTipText: ""
|
||||||
|
|
||||||
property color textColor
|
property color textColor: Style.ncTextColor
|
||||||
property color textColorHovered
|
property color textColorHovered: textColor
|
||||||
|
|
||||||
property color bgColor: "transparent"
|
property color bgColor: "transparent"
|
||||||
|
|
||||||
property bool bold: false
|
property bool bold: false
|
||||||
|
|
||||||
|
property real bgOpacity: 0.3
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: root.bgColor
|
color: root.bgColor
|
||||||
opacity: parent.hovered ? 1.0 : 0.3
|
opacity: parent.hovered ? 1.0 : bgOpacity
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +52,7 @@ Button {
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
source: root.hovered ? root.imageSourceHover : root.imageSource
|
source: root.hovered ? root.imageSourceHover : root.imageSource
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace OCC {
|
||||||
class ActivityLink
|
class ActivityLink
|
||||||
{
|
{
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
|
|
||||||
Q_PROPERTY(QString imageSource MEMBER _imageSource)
|
Q_PROPERTY(QString imageSource MEMBER _imageSource)
|
||||||
Q_PROPERTY(QString imageSourceHovered MEMBER _imageSourceHovered)
|
Q_PROPERTY(QString imageSourceHovered MEMBER _imageSourceHovered)
|
||||||
Q_PROPERTY(QString label MEMBER _label)
|
Q_PROPERTY(QString label MEMBER _label)
|
||||||
|
@ -115,6 +115,7 @@ public:
|
||||||
QString conversationToken;
|
QString conversationToken;
|
||||||
QString messageId;
|
QString messageId;
|
||||||
QString messageSent;
|
QString messageSent;
|
||||||
|
QString userAvatar;
|
||||||
};
|
};
|
||||||
|
|
||||||
Type _type;
|
Type _type;
|
||||||
|
|
|
@ -80,6 +80,7 @@ QHash<int, QByteArray> ActivityListModel::roleNames() const
|
||||||
roles[TalkNotificationConversationTokenRole] = "conversationToken";
|
roles[TalkNotificationConversationTokenRole] = "conversationToken";
|
||||||
roles[TalkNotificationMessageIdRole] = "messageId";
|
roles[TalkNotificationMessageIdRole] = "messageId";
|
||||||
roles[TalkNotificationMessageSentRole] = "messageSent";
|
roles[TalkNotificationMessageSentRole] = "messageSent";
|
||||||
|
roles[TalkNotificationUserAvatarRole] = "userAvatar";
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
@ -332,6 +333,8 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
||||||
return a._talkNotificationData.messageId;
|
return a._talkNotificationData.messageId;
|
||||||
case TalkNotificationMessageSentRole:
|
case TalkNotificationMessageSentRole:
|
||||||
return replyMessageSent(a);
|
return replyMessageSent(a);
|
||||||
|
case TalkNotificationUserAvatarRole:
|
||||||
|
return a._talkNotificationData.userAvatar;
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ public:
|
||||||
TalkNotificationConversationTokenRole,
|
TalkNotificationConversationTokenRole,
|
||||||
TalkNotificationMessageIdRole,
|
TalkNotificationMessageIdRole,
|
||||||
TalkNotificationMessageSentRole,
|
TalkNotificationMessageSentRole,
|
||||||
|
TalkNotificationUserAvatarRole,
|
||||||
};
|
};
|
||||||
Q_ENUM(DataRole)
|
Q_ENUM(DataRole)
|
||||||
|
|
||||||
|
|
|
@ -90,13 +90,30 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
||||||
auto *ai = qvariant_cast<AccountState *>(sender()->property(propertyAccountStateC));
|
auto *ai = qvariant_cast<AccountState *>(sender()->property(propertyAccountStateC));
|
||||||
|
|
||||||
ActivityList list;
|
ActivityList list;
|
||||||
|
ActivityList callList;
|
||||||
|
|
||||||
|
|
||||||
foreach (auto element, notifies) {
|
foreach (auto element, notifies) {
|
||||||
auto json = element.toObject();
|
auto json = element.toObject();
|
||||||
auto a = Activity::fromActivityJson(json, ai->account());
|
auto a = Activity::fromActivityJson(json, ai->account());
|
||||||
|
|
||||||
a._type = Activity::NotificationType;
|
a._type = Activity::NotificationType;
|
||||||
a._id = json.value("notification_id").toInt();
|
a._id = json.value("notification_id").toInt();
|
||||||
|
|
||||||
|
if(json.contains("subjectRichParameters")) {
|
||||||
|
const auto richParams = json.value("subjectRichParameters").toObject();
|
||||||
|
for(const auto &key : richParams.keys()) {
|
||||||
|
const auto parameterJsonObject = richParams.value(key).toObject();
|
||||||
|
a._subjectRichParameters.insert(key, Activity::RichSubjectParameter{
|
||||||
|
parameterJsonObject.value(QStringLiteral("type")).toString(),
|
||||||
|
parameterJsonObject.value(QStringLiteral("id")).toString(),
|
||||||
|
parameterJsonObject.value(QStringLiteral("name")).toString(),
|
||||||
|
QString(),
|
||||||
|
QUrl()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2 cases to consider:
|
// 2 cases to consider:
|
||||||
// 1. server == 24 & has Talk: object_type is chat/call/room & object_id contains conversationToken/messageId
|
// 1. server == 24 & has Talk: object_type is chat/call/room & object_id contains conversationToken/messageId
|
||||||
// 2. server < 24 & has Talk: object_type is chat/call/room & object_id contains _only_ conversationToken
|
// 2. server < 24 & has Talk: object_type is chat/call/room & object_id contains _only_ conversationToken
|
||||||
|
@ -116,7 +133,16 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
||||||
al._primary = true;
|
al._primary = true;
|
||||||
a._links.insert(0, al);
|
a._links.insert(0, al);
|
||||||
|
|
||||||
|
if(a._subjectRichParameters.contains("user")) {
|
||||||
|
a._talkNotificationData.userAvatar = ai->account()->url().toString() + QStringLiteral("/index.php/avatar/") + a._subjectRichParameters["user"].id + QStringLiteral("/128");
|
||||||
|
}
|
||||||
|
|
||||||
list.append(a);
|
list.append(a);
|
||||||
|
|
||||||
|
// We want to serve incoming call dialogs to the user for calls that
|
||||||
|
if(a._objectType == "call" && a._dateTime.secsTo(QDateTime::currentDateTime()) < 120) {
|
||||||
|
callList.append(a);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a._status = 0;
|
a._status = 0;
|
||||||
|
@ -145,6 +171,7 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
||||||
list.append(a);
|
list.append(a);
|
||||||
}
|
}
|
||||||
emit newNotificationList(list);
|
emit newNotificationList(list);
|
||||||
|
emit newIncomingCallsList(callList);
|
||||||
|
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newNotificationList(ActivityList);
|
void newNotificationList(ActivityList);
|
||||||
|
void newIncomingCallsList(ActivityList);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotFetchNotifications();
|
void slotFetchNotifications();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "guiutility.h"
|
#include "guiutility.h"
|
||||||
#include "syncfileitem.h"
|
#include "syncfileitem.h"
|
||||||
|
#include "systray.h"
|
||||||
#include "tray/activitylistmodel.h"
|
#include "tray/activitylistmodel.h"
|
||||||
#include "tray/notificationcache.h"
|
#include "tray/notificationcache.h"
|
||||||
#include "tray/unifiedsearchresultslistmodel.h"
|
#include "tray/unifiedsearchresultslistmodel.h"
|
||||||
|
@ -123,6 +124,18 @@ void User::slotBuildNotificationDisplay(const ActivityList &list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void User::slotBuildIncomingCallDialogs(const ActivityList &list)
|
||||||
|
{
|
||||||
|
const auto systray = Systray::instance();
|
||||||
|
const ConfigFile cfg;
|
||||||
|
|
||||||
|
if(systray && cfg.showCallNotifications()) {
|
||||||
|
for(const auto &activity : list) {
|
||||||
|
systray->createCallDialog(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void User::setNotificationRefreshInterval(std::chrono::milliseconds interval)
|
void User::setNotificationRefreshInterval(std::chrono::milliseconds interval)
|
||||||
{
|
{
|
||||||
if (!checkPushNotificationsAreReady()) {
|
if (!checkPushNotificationsAreReady()) {
|
||||||
|
@ -264,6 +277,8 @@ void User::slotRefreshNotifications()
|
||||||
auto *snh = new ServerNotificationHandler(_account.data());
|
auto *snh = new ServerNotificationHandler(_account.data());
|
||||||
connect(snh, &ServerNotificationHandler::newNotificationList,
|
connect(snh, &ServerNotificationHandler::newNotificationList,
|
||||||
this, &User::slotBuildNotificationDisplay);
|
this, &User::slotBuildNotificationDisplay);
|
||||||
|
connect(snh, &ServerNotificationHandler::newIncomingCallsList,
|
||||||
|
this, &User::slotBuildIncomingCallDialogs);
|
||||||
|
|
||||||
snh->slotFetchNotifications();
|
snh->slotFetchNotifications();
|
||||||
} else {
|
} else {
|
||||||
|
@ -906,7 +921,7 @@ void UserModel::addUser(AccountStatePtr &user, const bool &isCurrent)
|
||||||
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
ConfigFile cfg;
|
ConfigFile cfg;
|
||||||
_users.last()->setNotificationRefreshInterval(cfg.notificationRefreshInterval());
|
u->setNotificationRefreshInterval(cfg.notificationRefreshInterval());
|
||||||
emit newUserSelected();
|
emit newUserSelected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ public slots:
|
||||||
void slotNotifyServerFinished(const QString &reply, int replyCode);
|
void slotNotifyServerFinished(const QString &reply, int replyCode);
|
||||||
void slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
|
void slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
|
||||||
void slotBuildNotificationDisplay(const ActivityList &list);
|
void slotBuildNotificationDisplay(const ActivityList &list);
|
||||||
|
void slotBuildIncomingCallDialogs(const ActivityList &list);
|
||||||
void slotRefreshNotifications();
|
void slotRefreshNotifications();
|
||||||
void slotRefreshActivities();
|
void slotRefreshActivities();
|
||||||
void slotRefresh();
|
void slotRefresh();
|
||||||
|
|
|
@ -68,6 +68,7 @@ static const char monoIconsC[] = "monoIcons";
|
||||||
static const char promptDeleteC[] = "promptDeleteAllFiles";
|
static const char promptDeleteC[] = "promptDeleteAllFiles";
|
||||||
static const char crashReporterC[] = "crashReporter";
|
static const char crashReporterC[] = "crashReporter";
|
||||||
static const char optionalServerNotificationsC[] = "optionalServerNotifications";
|
static const char optionalServerNotificationsC[] = "optionalServerNotifications";
|
||||||
|
static const char showCallNotificationsC[] = "showCallNotifications";
|
||||||
static const char showInExplorerNavigationPaneC[] = "showInExplorerNavigationPane";
|
static const char showInExplorerNavigationPaneC[] = "showInExplorerNavigationPane";
|
||||||
static const char skipUpdateCheckC[] = "skipUpdateCheck";
|
static const char skipUpdateCheckC[] = "skipUpdateCheck";
|
||||||
static const char autoUpdateCheckC[] = "autoUpdateCheck";
|
static const char autoUpdateCheckC[] = "autoUpdateCheck";
|
||||||
|
@ -189,6 +190,19 @@ bool ConfigFile::optionalServerNotifications() const
|
||||||
return settings.value(QLatin1String(optionalServerNotificationsC), true).toBool();
|
return settings.value(QLatin1String(optionalServerNotificationsC), true).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConfigFile::showCallNotifications() const
|
||||||
|
{
|
||||||
|
const QSettings settings(configFile(), QSettings::IniFormat);
|
||||||
|
return settings.value(QLatin1String(showCallNotificationsC), true).toBool() && optionalServerNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigFile::setShowCallNotifications(bool show)
|
||||||
|
{
|
||||||
|
QSettings settings(configFile(), QSettings::IniFormat);
|
||||||
|
settings.setValue(QLatin1String(showCallNotificationsC), show);
|
||||||
|
settings.sync();
|
||||||
|
}
|
||||||
|
|
||||||
bool ConfigFile::showInExplorerNavigationPane() const
|
bool ConfigFile::showInExplorerNavigationPane() const
|
||||||
{
|
{
|
||||||
const bool defaultValue =
|
const bool defaultValue =
|
||||||
|
@ -557,7 +571,7 @@ chrono::milliseconds ConfigFile::notificationRefreshInterval(const QString &conn
|
||||||
QSettings settings(configFile(), QSettings::IniFormat);
|
QSettings settings(configFile(), QSettings::IniFormat);
|
||||||
settings.beginGroup(con);
|
settings.beginGroup(con);
|
||||||
|
|
||||||
auto defaultInterval = chrono::minutes(5);
|
const auto defaultInterval = chrono::minutes(1);
|
||||||
auto interval = millisecondsValue(settings, notificationRefreshIntervalC, defaultInterval);
|
auto interval = millisecondsValue(settings, notificationRefreshIntervalC, defaultInterval);
|
||||||
if (interval < chrono::minutes(1)) {
|
if (interval < chrono::minutes(1)) {
|
||||||
qCWarning(lcConfigFile) << "Notification refresh interval smaller than one minute, setting to one minute";
|
qCWarning(lcConfigFile) << "Notification refresh interval smaller than one minute, setting to one minute";
|
||||||
|
|
|
@ -152,6 +152,9 @@ public:
|
||||||
bool optionalServerNotifications() const;
|
bool optionalServerNotifications() const;
|
||||||
void setOptionalServerNotifications(bool show);
|
void setOptionalServerNotifications(bool show);
|
||||||
|
|
||||||
|
bool showCallNotifications() const;
|
||||||
|
void setShowCallNotifications(bool show);
|
||||||
|
|
||||||
bool showInExplorerNavigationPane() const;
|
bool showInExplorerNavigationPane() const;
|
||||||
void setShowInExplorerNavigationPane(bool show);
|
void setShowInExplorerNavigationPane(bool show);
|
||||||
|
|
||||||
|
|
|
@ -232,5 +232,6 @@
|
||||||
<file>theme/black/edit.svg</file>
|
<file>theme/black/edit.svg</file>
|
||||||
<file>theme/delete.svg</file>
|
<file>theme/delete.svg</file>
|
||||||
<file>theme/send.svg</file>
|
<file>theme/send.svg</file>
|
||||||
|
<file>theme/call-notification.wav</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -30,7 +30,7 @@ QtObject {
|
||||||
property int trayWindowWidth: variableSize(400)
|
property int trayWindowWidth: variableSize(400)
|
||||||
property int trayWindowHeight: variableSize(510)
|
property int trayWindowHeight: variableSize(510)
|
||||||
property int trayWindowRadius: 10
|
property int trayWindowRadius: 10
|
||||||
property int trayWindowBorderWidth: 1
|
property int trayWindowBorderWidth: variableSize(1)
|
||||||
property int trayWindowHeaderHeight: variableSize(60)
|
property int trayWindowHeaderHeight: variableSize(60)
|
||||||
property int trayHorizontalMargin: 10
|
property int trayHorizontalMargin: 10
|
||||||
property int trayListItemIconSize: accountAvatarSize
|
property int trayListItemIconSize: accountAvatarSize
|
||||||
|
@ -68,6 +68,9 @@ QtObject {
|
||||||
property int activityItemActionPrimaryButtonMinWidth: 100
|
property int activityItemActionPrimaryButtonMinWidth: 100
|
||||||
property int activityItemActionSecondaryButtonMinWidth: 80
|
property int activityItemActionSecondaryButtonMinWidth: 80
|
||||||
|
|
||||||
|
property int callNotificationPrimaryButtonMinWidth: 100
|
||||||
|
property int callNotificationPrimaryButtonMinHeight: 40
|
||||||
|
|
||||||
property int roundButtonBackgroundVerticalMargins: 10
|
property int roundButtonBackgroundVerticalMargins: 10
|
||||||
property int roundedButtonBackgroundVerticalMargins: 5
|
property int roundedButtonBackgroundVerticalMargins: 5
|
||||||
|
|
||||||
|
|
BIN
theme/call-notification.wav
Normal file
BIN
theme/call-notification.wav
Normal file
Binary file not shown.
Loading…
Reference in a new issue