Пишемо клієнт для Slack з оповіщеннями

Вітаю, Хабравчане! Сьогодні Slack випустили свій клієнт для Windows. Але ще зовсім недавно такого клієнта не було, і необхідність отримувати нормальні повідомлення було необхідністю. Slack пропонував використовувати додаток Chrome. У даного підходу було два мінуси:
  1. Відсутність можливості налаштувати, скільки часу буде показуватися повідомлення
  2. Якщо повідомлення пропало, то користувач ніяк про це не дізнається.


Приміром, ти пішов налити собі каву, а тут хтось написав у чат. Повертаєшся на робоче місце і… тиша! Нічого не відбувалося. Ти працюєш далі, а чоловік все чекає і чекає, поки хто-небудь йому відповість. Непорядок! Skype ввічливо повідомляє тебе спливаючим вікном і нахабно сигналізує в таскбаре про те, що тобі прийшло повідомлення. Швидше прочитай, а то твій таскбара так і будемо мигати жовтим світлом. Навіть якщо ти пішов на весь день.

Відмотаємо час на 1 місяць тому. Йдемо на сторінку myawesometeam.slack.com/apps і бачимо відсутність нативного клієнта для Windows і Linux, замість цього там програми для Chrome. Засмучуємося. Запускаємо додаток, розуміємо всю сумне буття.

Я почав шукати вирішення проблеми. Першим знайшовся SlackUI. Він побудований на базі CEF (Chromium Embedded Framework). Я вже було майже зрадів, запустив клієнт і побачив все те ж саме, що і в додатку Chrome. Повідомлення пропадають через 10 секунд, жодних повідомлень про те, що щось було, поки ти ходив за кавою.

Що ж, я почав гуглити і натрапив на те, що в Qt WebKit можна написати свій плагін, у тому числі і для повідомлень. Знайшовся проект QupZilla і його плагіни для Qt WebKit (https://github.com/QupZilla/qtwebkit-plugins). Це було те, що потрібно!

Етап 1. Робимо плагін повідомлень Qt WebKit
У файлі .pri нам потрібно додати відмінності файли для плагіна і відмінності файли Qt, щоб він міг підхопити наш плагін:
HEADERS += $$PWD/qtwebkitplugin.h \
$$[QT_INSTALL_HEADERS]/QtWebKit/qwebkitplatformplugin.h

SOURCES += $$PWD/qtwebkitplugin.cpp

DEFINES *= QT_STATICPLUGIN

Код самого плагіна:
qtwebkitplugin.h
#include "qwebkitplatformplugin.h"

class QtWebKitPlugin : public QObject, public QWebKitPlatformPlugin
{
Q_OBJECT
Q_INTERFACES(QWebKitPlatformPlugin)

#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID "org.qtwebkit.QtWebKit.QtWebKitPlugins")
#endif

public:
explicit QtWebKitPlugin();

bool supportsExtension(Extension ext) const;
QObject* createExtension(Extension ext) const;
};



qtwebkitplugin.cpp
bool QtWebKitPlugin::supportsExtension(Extension ext) const
{
return (ext == Notifications);
}

QObject* QtWebKitPlugin::createExtension(Extension ext) const
{
switch (ext) {
case Notifications:
return new NotificationPresenter();

default:
return 0;
}
}

#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(qtwebkitplugins, QtWebKitPlugin)
#endif

#if (QT_VERSION < 0x050000)
Q_IMPORT_PLUGIN(qtwebkitplugins)
#else
Q_IMPORT_PLUGIN(QtWebKitPlugin)
#endif



Поки що нічого складного немає. NotificationPresenter — це якийсь клас, який буде відображати наші попередження, нехай навіть і консоль відладки:

notificationpresenter.h
#include "qwebkitplatformplugin.h"

class NotificationPresenter : public QWebNotificationPresenter
{

public:
explicit NotificationPresenter();

void showNotification(const QWebNotificationData* data);
};


notificationpresenter.cpp
NotificationPresenter::NotificationPresenter()
: QWebNotificationPresenter()
{
}

void NotificationPresenter::showNotification(const QWebNotificationData* data)
{
qDebug() << "--------------------------";
qDebug() << "Title:";
qDebug() << data->title();
qDebug() << "Message:";
qDebug() << data->message();
qDebug() << "--------------------------";
}




Етап 2. Додаємо QWebView
Підключаємо .pri файл нашого проекту і додаємо в .pro файл залежність від webkitwidgets:
...
QT += webkitwidgets
include(plugins/qtwebkit/qtwebkit-plugins.pri)
...


Додаємо на яку-небудь форму QWebView, після чого нам треба його трохи налаштувати і підписатися на подію featurePermissionRequested:

код?
void MainWindow::createWebView()
{
webview->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
webview->settings()->setAttribute(QWebSettings::NotificationsEnabled, true);
connect(webView->page(), SIGNAL(featurePermissionRequested(QWebFrame*,QWebPage::Feature)),
this, SLOT(featureRequest(QWebFrame*,QWebPage::Feature)));
}

void MainWindow::featureRequest(QWebFrame *frame, QWebPage::Feature feature)
{
qDebug() << frame->url();

if (feature == QWebPage::Feature::Notifications)
{
int result = QMessageBox::question(this,
QString("Notification permission"),
QString("%1\nasks notifications for persmission. Should I allow?").arg(frame->url().toString()),
QMessageBox::StandardButton::Ok, QMessageBox::Cancel);

if (result == QMessageBox::StandardButton::Ok)
{
webView->page()->setFeaturePermission(frame, feature,
QWebPage::PermissionPolicy::PermissionGrantedByUser);
}
}
}



Задаємо тестовий url з повідомленням (наприклад ось цей) і тестуємо сторінку. Має показуватися повідомлення.

Етап 3. Додаємо куки і кеш на диск. Додаємо рідні шрифти
В першу чергу бісять криві шрифти. Виправимо їх одразу
webView->settings()->setFontFamily(QWebSettings::StandardFont, "Segoe UI");
webView->settings()->setFontSize(QWebSettings::DefaultFontSize, 16);


Якщо ми тепер перезапустим додаток, то нам знову потрібно повторити процедуру підтвердження, заново вводити паролі і т. п. Значить, настав час для того, щоб зберігати куки і кеш на диску.
З кешем трохи простіше:
void MainWindow::setStoragePath()
{
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
qDebug() << "Cache path" << path;

storagePath = path;
webView->page()->settings()->enablePersistentStorage(path);
}

З зберіганням куки складніше. Я просто знайшов готове рішення з прикладів Qt. Можна легко знайти в гуглі за словами QWebView і CookieJar. Приклад CookieJar можна знайти в исходниках проекту
void MainWindow::setCookies()
{
if (!cookieJar)
{
cookieJar = new CookieJar(this);
}

webView->page()->networkAccessManager()->setCookieJar(cookieJar);
}

Після цього куки і кеш повинні зберігатися і не потрібно щоразу вводити логіни і паролі.

Етап 4. Підключаємо додаткові бібліотеки
Для повідомлень я вирішив використовувати Aseman Qt Tools.
Завантажуємо, підключаємо в .pro файл
include(asemantools/asemantools.pri)


Тепер у NotificationPresenter нашого плагіна потрібно протягнути якийсь інтерфейс для відображення повідомлень.
Додаємо в qtwebkit.pri
INCLUDEPATH += $$top_srcdir
HEADERS += $$top_srcdir/mainapplication.h


Тут MainApplication — спадкоємець від QApplication. Додаємо функції відображення повідомлень:

notificationpresenter.cpp
void NotificationPresenter::showNotification(const QWebNotificationData* data)
{
mApp->showNotification(data->title(), data->message());
}



mainapplication.h
#include <QApplication>
#include "mainwindow.h"

#define mApp ((MainApplication*)MainApplication::instance())

class MainApplication : public QApplication
{
Q_OBJECT

public:
explicit MainApplication(int &argc, char** argv);

void setMainWindow(MainWindow* window);
MainWindow* getMainWindow();

void showNotification(QString title, QString message);

~MainApplication();
private:
MainWindow *m_window = 0;
};



mainapplication.cpp
MainWindow *MainApplication::getMainWindow()
{
if (!m_window){
m_window = new MainWindow();
}

return m_window;
}

void MainApplication::showNotification(QString title, QString message)
{
getMainWindow()->showNotification(title, message);
}



Додаємо AsemanNotification в головне вікно програми і змушуємо таскбара мигати жовтим світлом:
MainWindow::MainWindow() {
// ...
notification = new AsemanNativeNotification(this);
// ...
}

void MainWindow::showNotification(QString title, QString message)
{
notification->sendNotify(title, message, "://images/png/Slack.png", 0, 100000); // Показуємо повідомлення
QApplication::alert(this); // Моргаємо таскбаром
}

Компілюємо. Запускаємо тестову сторінку. Повинні з'явитися справжні повідомлення.

Етап 5. Пробуємо зібрати під лінукс
І тут ми приїхали. В Linux різні DE і все буде працювати догори дригом.
У Unity іконка трея буде показуватися в лівому верхньому кутку екрану. Виглядає це приблизно так:

У Gnome 3 повідомлення з AsemanTools у мене постійно тікали кудись за межі екрану. Виразних рішень я знайшов і знову за лінукс стало сумно і прикро. Нічого не працює з коробки, потрібно вічні танці з бубном

Підсумки
У результаті отриманий досвід створення програми на основі WebKit, а також створення плагінів для Qt.
Результат роботи:


Посилання на отриманий проект на Github

Настав час трохи привести код в порядок, заварити каву і повернутися до комп'ютера, де радісно блимає жовтий значок Slack в таскбаре. Запушить всі зміни і чекати, коли райдужний єдиноріг перевірить твій проект, щоб ткнути тебе головою в…

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.