diff --git a/interface/resources/sounds/mention/Mentioned A.wav b/interface/resources/sounds/mention/Mentioned A.wav new file mode 100644 index 0000000000..5b203d7adf Binary files /dev/null and b/interface/resources/sounds/mention/Mentioned A.wav differ diff --git a/interface/resources/sounds/mention/Mentioned B.wav b/interface/resources/sounds/mention/Mentioned B.wav new file mode 100644 index 0000000000..22a3b8bb64 Binary files /dev/null and b/interface/resources/sounds/mention/Mentioned B.wav differ diff --git a/interface/resources/sounds/mention/Mentioned C.wav b/interface/resources/sounds/mention/Mentioned C.wav new file mode 100644 index 0000000000..78d5fe3f66 Binary files /dev/null and b/interface/resources/sounds/mention/Mentioned C.wav differ diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f7ce6a60ff..0c1c7fd3b5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -215,6 +215,10 @@ Menu::Menu() : toggleChat(); connect(&xmppClient, SIGNAL(connected()), this, SLOT(toggleChat())); connect(&xmppClient, SIGNAL(disconnected()), this, SLOT(toggleChat())); + + QDir::setCurrent(Application::resourcesPath()); + // init chat window to listen chat + _chatWindow = new ChatWindow(Application::getInstance()->getWindow()); #endif QMenu* viewMenu = addMenu("View"); diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index 48c1f5d976..666906681c 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -36,6 +36,7 @@ void XmppClient::xmppConnected() { _publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); _publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername()); _publicChatRoom->join(); + emit joinedPublicChatRoom(); } void XmppClient::xmppError(QXmppClient::Error error) { diff --git a/interface/src/XmppClient.h b/interface/src/XmppClient.h index 8af3204377..cbb06cf992 100644 --- a/interface/src/XmppClient.h +++ b/interface/src/XmppClient.h @@ -28,6 +28,9 @@ public: QXmppClient& getXMPPClient() { return _xmppClient; } const QXmppMucRoom* getPublicChatRoom() const { return _publicChatRoom; } +signals: + void joinedPublicChatRoom(); + private slots: void xmppConnected(); void xmppError(QXmppClient::Error error); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 3f2df5593b..39db2b734e 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -26,17 +26,23 @@ #include "ChatWindow.h" + + const int NUM_MESSAGES_TO_TIME_STAMP = 20; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); const QRegularExpression regexHifiLinks("([#@]\\S+)"); +const QString mentionSoundsPath("/sounds/mention/"); +const QString mentionRegex("@(\\b%1\\b)"); ChatWindow::ChatWindow(QWidget* parent) : FramelessDialog(parent, 0, POSITION_RIGHT), ui(new Ui::ChatWindow), numMessagesAfterLastTimeStamp(0), _mousePressed(false), - _mouseStartPosition() + _mouseStartPosition(), + _trayIcon(parent), + _effectPlayer() { setAttribute(Qt::WA_DeleteOnClose, false); @@ -77,16 +83,47 @@ ChatWindow::ChatWindow(QWidget* parent) : ui->usersWidget->hide(); ui->messagesScrollArea->hide(); ui->messagePlainTextEdit->hide(); - connect(&xmppClient, SIGNAL(connected()), this, SLOT(connected())); + connect(&XmppClient::getInstance(), SIGNAL(joinedPublicChatRoom()), this, SLOT(connected())); } connect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); + connect(&_trayIcon, SIGNAL(messageClicked()), this, SLOT(notificationClicked())); #endif + + QDir mentionSoundsDir(Application::resourcesPath() + mentionSoundsPath); + _mentionSounds = mentionSoundsDir.entryList(QDir::Files); + _trayIcon.setIcon(QIcon( Application::resourcesPath() + "/images/hifi-logo.svg")); +} + +void ChatWindow::notificationClicked() { + if (parentWidget()->isMinimized()) { + parentWidget()->showNormal(); + } + if (isHidden()) { + show(); + } + + // find last mention + int messageCount = ui->messagesVBoxLayout->count(); + for (unsigned int i = messageCount; i > 0; i--) { + ChatMessageArea* area = (ChatMessageArea*)ui->messagesVBoxLayout->itemAt(i - 1)->widget(); + QRegularExpression usernameMention(mentionRegex.arg(AccountManager::getInstance().getAccountInfo().getUsername())); + if (area->toPlainText().contains(usernameMention)) { + int top = area->geometry().top(); + int height = area->geometry().height(); + + QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); + verticalScrollBar->setSliderPosition(top - verticalScrollBar->size().height() + height); + return; + } + } + + scrollToBottom(); } ChatWindow::~ChatWindow() { #ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); - disconnect(&xmppClient, SIGNAL(connected()), this, SLOT(connected())); + disconnect(&xmppClient, SIGNAL(joinedPublicChatRoom()), this, SLOT(connected())); disconnect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); @@ -105,9 +142,15 @@ void ChatWindow::keyPressEvent(QKeyEvent* event) { void ChatWindow::showEvent(QShowEvent* event) { FramelessDialog::showEvent(event); + if (!event->spontaneous()) { ui->messagePlainTextEdit->setFocus(); } + + const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); + if (xmppClient.isConnected()) { + participantsChanged(); + } } bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { @@ -304,6 +347,21 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { } else { lastMessageStamp = QDateTime::currentDateTime(); } + + QRegularExpression usernameMention(mentionRegex.arg(AccountManager::getInstance().getAccountInfo().getUsername())); + if (isHidden() && message.body().contains(usernameMention)) { + if (_effectPlayer.state() != QMediaPlayer::PlayingState) { + // get random sound + QFileInfo inf = QFileInfo(Application::resourcesPath() + + mentionSoundsPath + + _mentionSounds.at(rand() % _mentionSounds.size())); + _effectPlayer.setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); + _effectPlayer.play(); + } + + _trayIcon.show(); + _trayIcon.showMessage(windowTitle(), message.body()); + } } #endif diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index 104fbe1746..1e0f533e9e 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include @@ -63,6 +65,9 @@ private: QDateTime lastMessageStamp; bool _mousePressed; QPoint _mouseStartPosition; + QSystemTrayIcon _trayIcon; + QStringList _mentionSounds; + QMediaPlayer _effectPlayer; private slots: void connected(); @@ -71,6 +76,7 @@ private slots: void error(QXmppClient::Error error); void participantsChanged(); void messageReceived(const QXmppMessage& message); + void notificationClicked(); #endif }; diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index 278e3c5dab..8a84956269 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -198,9 +198,6 @@ color: #0e7077 25 - - - @@ -510,7 +507,7 @@ color: #0e7077 - + 0 @@ -547,7 +544,7 @@ color: #0e7077 - + @@ -564,7 +561,7 @@ color: #0e7077 - + Qt::Horizontal