diff --git a/db/emojis.db b/db/emojis.db
new file mode 100644
index 0000000..397eae8
Binary files /dev/null and b/db/emojis.db differ
diff --git a/harbour-fernschreiber.pro b/harbour-fernschreiber.pro
index 359923b..75db4e6 100644
--- a/harbour-fernschreiber.pro
+++ b/harbour-fernschreiber.pro
@@ -16,7 +16,7 @@ CONFIG += sailfishapp sailfishapp_i18n
PKGCONFIG += nemonotifications-qt5 ngf-qt5
-QT += core dbus
+QT += core dbus sql
SOURCES += src/harbour-fernschreiber.cpp \
src/appsettings.cpp \
@@ -24,6 +24,7 @@ SOURCES += src/harbour-fernschreiber.cpp \
src/chatmodel.cpp \
src/dbusadaptor.cpp \
src/dbusinterface.cpp \
+ src/emojisearchworker.cpp \
src/fernschreiberutils.cpp \
src/notificationmanager.cpp \
src/processlauncher.cpp \
@@ -102,8 +103,11 @@ ICONPATH = /usr/share/icons/hicolor
fernschreiber.desktop.path = /usr/share/applications/
fernschreiber.desktop.files = harbour-fernschreiber.desktop
+database.files = db
+database.path = /usr/share/$${TARGET}
+
INSTALLS += telegram 86.png 108.png 128.png 172.png 256.png \
- fernschreiber.desktop gui images
+ fernschreiber.desktop gui images database
HEADERS += \
src/appsettings.h \
@@ -111,6 +115,7 @@ HEADERS += \
src/chatmodel.h \
src/dbusadaptor.h \
src/dbusinterface.h \
+ src/emojisearchworker.h \
src/fernschreiberutils.h \
src/notificationmanager.h \
src/processlauncher.h \
diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml
index fa8d37a..0e51efc 100644
--- a/qml/pages/ChatPage.qml
+++ b/qml/pages/ChatPage.qml
@@ -43,6 +43,7 @@ Page {
property variant chatPartnerInformation;
property variant chatGroupInformation;
property int chatOnlineMemberCount: 0;
+ property variant emojiProposals;
function updateChatPartnerStatusText() {
if (chatPartnerInformation.status['@type'] === "userStatusEmpty" ) {
@@ -176,6 +177,44 @@ Page {
controlSendButton();
}
+ function getWordBoundaries(text, cursorPosition) {
+ var wordBoundaries = { beginIndex : 0, endIndex : text.length};
+ var currentIndex = 0;
+ for (currentIndex = (cursorPosition - 1); currentIndex > 0; currentIndex--) {
+ if (text.charAt(currentIndex) === ' ') {
+ wordBoundaries.beginIndex = currentIndex + 1;
+ break;
+ }
+ }
+ for (currentIndex = cursorPosition; currentIndex < text.length; currentIndex++) {
+ if (text.charAt(currentIndex) === ' ') {
+ wordBoundaries.endIndex = currentIndex;
+ break;
+ }
+ }
+ return wordBoundaries;
+ }
+
+ function handleMessageTextReplacement(text, cursorPosition) {
+ var wordBoundaries = getWordBoundaries(text, cursorPosition);
+
+ var currentWord = text.substring(wordBoundaries.beginIndex, wordBoundaries.endIndex);
+ if (currentWord.length > 1 && currentWord.charAt(0) === ':') {
+ tdLibWrapper.searchEmoji(currentWord.substring(1));
+ } else {
+ chatPage.emojiProposals = null;
+ }
+ }
+
+ function replaceMessageText(text, cursorPosition, newText) {
+ var wordBoundaries = getWordBoundaries(text, cursorPosition);
+ var newCompleteText = text.substring(0, wordBoundaries.beginIndex) + newText + " " + text.substring(wordBoundaries.endIndex);
+ var newIndex = wordBoundaries.beginIndex + newText.length + 1;
+ newMessageTextField.text = newCompleteText;
+ newMessageTextField.cursorPosition = newIndex;
+ lostFocusTimer.start();
+ }
+
Component.onCompleted: {
initializePage();
}
@@ -227,6 +266,9 @@ Page {
uploadingProgressBar.maximumValue = fileInformation.size;
uploadingProgressBar.value = fileInformation.remote.uploaded_size;
}
+ onEmojiSearchSuccessful: {
+ chatPage.emojiProposals = result;
+ }
}
Connections {
@@ -269,6 +311,26 @@ Page {
}
}
+ Timer {
+ id: lostFocusTimer
+ interval: 200
+ running: false
+ repeat: false
+ onTriggered: {
+ newMessageTextField.forceActiveFocus();
+ }
+ }
+
+ Timer {
+ id: textReplacementTimer
+ interval: 600
+ running: false
+ repeat: false
+ onTriggered: {
+ handleMessageTextReplacement(newMessageTextField.text, newMessageTextField.cursorPosition);
+ }
+ }
+
Timer {
id: chatContactTimeUpdater
interval: 60000
@@ -1151,6 +1213,58 @@ Page {
}
+ Column {
+ id: emojiColumn
+ width: parent.width
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: emojiProposals ? ( emojiProposals.length > 0 ? true : false ) : false
+ opacity: emojiProposals ? ( emojiProposals.length > 0 ? 1 : 0 ) : 0
+ Behavior on opacity { NumberAnimation {} }
+ spacing: Theme.paddingMedium
+
+ Flickable {
+ width: parent.width
+ height: emojiResultRow.height + Theme.paddingSmall
+ anchors.horizontalCenter: parent.horizontalCenter
+ contentWidth: emojiResultRow.width
+ clip: true
+ Row {
+ id: emojiResultRow
+ spacing: Theme.paddingMedium
+ Repeater {
+ model: emojiProposals
+
+ Item {
+ height: singleEmojiRow.height
+ width: singleEmojiRow.width
+
+ Row {
+ id: singleEmojiRow
+ spacing: Theme.paddingSmall
+
+ Image {
+ id: emojiPicture
+ source: "../js/emoji/" + modelData.file_name
+ width: Theme.fontSizeLarge
+ height: Theme.fontSizeLarge
+ }
+
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ replaceMessageText(newMessageTextField.text, newMessageTextField.cursorPosition, modelData.emoji);
+ emojiProposals = null;
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
Text {
width: parent.width
@@ -1199,6 +1313,7 @@ Page {
onTextChanged: {
controlSendButton();
+ textReplacementTimer.restart();
}
}
diff --git a/rpm/harbour-fernschreiber.spec b/rpm/harbour-fernschreiber.spec
index fa78aed..2054851 100644
--- a/rpm/harbour-fernschreiber.spec
+++ b/rpm/harbour-fernschreiber.spec
@@ -24,6 +24,7 @@ BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: pkgconfig(Qt5DBus)
+BuildRequires: pkgconfig(Qt5Sql)
BuildRequires: pkgconfig(nemonotifications-qt5)
BuildRequires: pkgconfig(ngf-qt5)
BuildRequires: desktop-file-utils
diff --git a/rpm/harbour-fernschreiber.yaml b/rpm/harbour-fernschreiber.yaml
index 0524663..21c163a 100644
--- a/rpm/harbour-fernschreiber.yaml
+++ b/rpm/harbour-fernschreiber.yaml
@@ -24,6 +24,7 @@ PkgConfigBR:
- Qt5Qml
- Qt5Quick
- Qt5DBus
+ - Qt5Sql
- nemonotifications-qt5
- ngf-qt5
diff --git a/src/emojisearchworker.cpp b/src/emojisearchworker.cpp
new file mode 100644
index 0000000..5e97d0b
--- /dev/null
+++ b/src/emojisearchworker.cpp
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2020 Sebastian J. Wolf
+
+ This file is part of Fernschreiber.
+
+ Fernschreiber is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fernschreiber is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fernschreiber. If not, see .
+*/
+#include "emojisearchworker.h"
+
+#define LOG(x) qDebug() << "[EmojiSearchWorker]" << x
+
+EmojiSearchWorker::~EmojiSearchWorker()
+{
+ LOG("Destroying myself...");
+ database.close();
+}
+
+EmojiSearchWorker::EmojiSearchWorker(QObject *parent) : QThread(parent)
+{
+ LOG("Initializing Emoji database");
+ QSqlDatabase::removeDatabase("emojis");
+ database = QSqlDatabase::addDatabase("QSQLITE", "emojis");
+ database.setDatabaseName("/usr/share/harbour-fernschreiber/db/emojis.db");
+}
+
+void EmojiSearchWorker::setParameters(const QString &queryString)
+{
+ this->queryString = queryString;
+}
+
+void EmojiSearchWorker::performSearch()
+{
+ LOG("Performing emoji search" << this->queryString);
+ QVariantList resultList;
+
+ if (database.open()) {
+ QSqlQuery query(database);
+ query.prepare("select * from emojis where description match (:queryString) limit 25");
+ query.bindValue(":queryString", queryString + "*");
+ query.exec();
+ while (query.next()) {
+ if (isInterruptionRequested()) {
+ break;
+ }
+ QVariantMap foundEmoji;
+ foundEmoji.insert("file_name", query.value(0).toString());
+ foundEmoji.insert("emoji", query.value(1).toString());
+ foundEmoji.insert("emoji_version", query.value(2).toString());
+ foundEmoji.insert("description", query.value(3).toString());
+ resultList.append(foundEmoji);
+ }
+ database.close();
+ } else {
+ LOG("Unable to perform a query on database" << database.lastError().databaseText());
+ }
+
+ emit searchCompleted(queryString, resultList);
+}
diff --git a/src/emojisearchworker.h b/src/emojisearchworker.h
new file mode 100644
index 0000000..c728b07
--- /dev/null
+++ b/src/emojisearchworker.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2020 Sebastian J. Wolf
+
+ This file is part of Fernschreiber.
+
+ Fernschreiber is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fernschreiber is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fernschreiber. If not, see .
+*/
+#ifndef EMOJISEARCHWORKER_H
+#define EMOJISEARCHWORKER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+class EmojiSearchWorker : public QThread
+{
+ Q_OBJECT
+ void run() Q_DECL_OVERRIDE {
+ performSearch();
+ }
+public:
+ ~EmojiSearchWorker() override;
+ explicit EmojiSearchWorker(QObject *parent = nullptr);
+ void setParameters(const QString &queryString);
+
+signals:
+ void searchCompleted(const QString &queryString, const QVariantList &resultList);
+
+public slots:
+
+private:
+ QSqlDatabase database;
+ QString queryString;
+
+ void performSearch();
+};
+
+#endif // EMOJISEARCHWORKER_H
diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp
index 447e1b6..d5b534b 100644
--- a/src/tdlibwrapper.cpp
+++ b/src/tdlibwrapper.cpp
@@ -93,6 +93,8 @@ TDLibWrapper::TDLibWrapper(QObject *parent) : QObject(parent)
connect(this->tdLibReceiver, SIGNAL(stickerSets(QVariantList)), this, SLOT(handleStickerSets(QVariantList)));
connect(this->tdLibReceiver, SIGNAL(stickerSet(QVariantMap)), this, SLOT(handleStickerSet(QVariantMap)));
+ connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
+
this->tdLibReceiver->start();
this->setLogVerbosityLevel();
@@ -467,6 +469,16 @@ void TDLibWrapper::getStickerSet(const QString &setId)
this->sendRequest(requestObject);
}
+void TDLibWrapper::searchEmoji(const QString &queryString)
+{
+ LOG("Searching emoji" << queryString);
+ while (this->emojiSearchWorker.isRunning()) {
+ this->emojiSearchWorker.requestInterruption();
+ }
+ this->emojiSearchWorker.setParameters(queryString);
+ this->emojiSearchWorker.start();
+}
+
QVariantMap TDLibWrapper::getUserInformation()
{
return this->userInformation;
@@ -828,6 +840,12 @@ void TDLibWrapper::handleStickerSet(const QVariantMap &stickerSet)
emit stickerSetReceived(stickerSet);
}
+void TDLibWrapper::handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList)
+{
+ LOG("Emoji search completed" << queryString);
+ emit emojiSearchSuccessful(resultList);
+}
+
void TDLibWrapper::setInitialParameters()
{
LOG("Sending initial parameters to TD Lib");
diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h
index a583505..6723470 100644
--- a/src/tdlibwrapper.h
+++ b/src/tdlibwrapper.h
@@ -24,6 +24,7 @@
#include "tdlibreceiver.h"
#include "dbusadaptor.h"
#include "dbusinterface.h"
+#include "emojisearchworker.h"
class TDLibWrapper : public QObject
{
@@ -129,6 +130,9 @@ public:
Q_INVOKABLE void getInstalledStickerSets();
Q_INVOKABLE void getStickerSet(const QString &setId);
+ // Others (candidates for extraction ;))
+ Q_INVOKABLE void searchEmoji(const QString &queryString);
+
public:
const Group* getGroup(qlonglong groupId) const;
static ChatMemberStatus chatMemberStatusFromString(const QString &status);
@@ -169,6 +173,7 @@ signals:
void installedStickerSetsUpdated(const QVariantList &stickerSetIds);
void stickerSetsReceived(const QVariantList &stickerSets);
void stickerSetReceived(const QVariantMap &stickerSet);
+ void emojiSearchSuccessful(const QVariantList &result);
public slots:
void handleVersionDetected(const QString &version);
@@ -204,6 +209,7 @@ public slots:
void handleInstalledStickerSetsUpdated(const QVariantList &stickerSetIds);
void handleStickerSets(const QVariantList &stickerSets);
void handleStickerSet(const QVariantMap &stickerSet);
+ void handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList);
private:
void setInitialParameters();
@@ -228,6 +234,8 @@ private:
QVariantMap unreadChatInformation;
QHash basicGroups;
QHash superGroups;
+ EmojiSearchWorker emojiSearchWorker;
+
};
#endif // TDLIBWRAPPER_H