diff options
author | Felix Weilbach <felix.weilbach@nextcloud.com> | 2021-09-09 12:18:22 +0300 |
---|---|---|
committer | Felix Weilbach <felix.weilbach@nextcloud.com> | 2021-09-09 12:18:22 +0300 |
commit | 8a8d488454405356b5d11f63bebff2d69be43b02 (patch) | |
tree | afa6c5a496561e6778227ece22a6c871622af926 | |
parent | f34d66302942ede371bf7bc5d5e213b6c41ea5d8 (diff) |
Add dialog to set user status
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
42 files changed, 4592 insertions, 294 deletions
diff --git a/resources.qrc b/resources.qrc index 9fa393c4e..f811db0f7 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,5 +1,9 @@ <RCC> <qresource prefix="/qml"> + <file>src/gui/UserStatusSelector.qml</file> + <file>src/gui/UserStatusSelectorDialog.qml</file> + <file>src/gui/EmojiPicker.qml</file> + <file>src/gui/ErrorBox.qml</file> <file>src/gui/tray/Window.qml</file> <file>src/gui/tray/UserLine.qml</file> <file>src/gui/tray/HeaderButton.qml</file> diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 67a2c7323..74c460d06 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -35,6 +35,8 @@ set(client_UI_SRCS addcertificatedialog.ui proxyauthdialog.ui mnemonicdialog.ui + UserStatusSelector.qml + UserStatusSelectorDialog.qml tray/ActivityActionButton.qml tray/ActivityItem.qml tray/Window.qml @@ -92,7 +94,6 @@ set(client_SRCS systray.cpp thumbnailjob.cpp userinfo.cpp - userstatus.cpp accountstate.cpp addcertificatedialog.cpp authenticationdialog.cpp @@ -106,6 +107,8 @@ set(client_SRCS iconjob.cpp iconutils.cpp remotewipe.cpp + userstatusselectormodel.cpp + emojimodel.cpp tray/ActivityData.cpp tray/ActivityListModel.cpp tray/UserModel.cpp diff --git a/src/gui/EmojiPicker.qml b/src/gui/EmojiPicker.qml new file mode 100644 index 000000000..6a66dbdcb --- /dev/null +++ b/src/gui/EmojiPicker.qml @@ -0,0 +1,112 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import com.nextcloud.desktopclient 1.0 as NC + +ColumnLayout { + NC.EmojiModel { + id: emojiModel + } + + signal chosen(string emoji) + + spacing: 0 + + FontMetrics { + id: metrics + } + + ListView { + id: headerLayout + Layout.fillWidth: true + implicitWidth: contentItem.childrenRect.width + implicitHeight: metrics.height * 2 + + orientation: ListView.Horizontal + + model: emojiModel.emojiCategoriesModel + + delegate: ItemDelegate { + width: metrics.height * 2 + height: headerLayout.height + + contentItem: Text { + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: emoji + } + + Rectangle { + anchors.bottom: parent.bottom + + width: parent.width + height: 2 + + visible: ListView.isCurrentItem + + color: "grey" + } + + + onClicked: { + emojiModel.setCategory(label) + } + } + + } + + Rectangle { + height: 1 + Layout.fillWidth: true + color: "grey" + } + + GridView { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredHeight: metrics.height * 8 + + cellWidth: metrics.height * 2 + cellHeight: metrics.height * 2 + + boundsBehavior: Flickable.DragOverBounds + clip: true + + model: emojiModel.model + + delegate: ItemDelegate { + + width: metrics.height * 2 + height: metrics.height * 2 + + contentItem: Text { + anchors.centerIn: parent + text: modelData === undefined ? "" : modelData.unicode + } + + onClicked: { + chosen(modelData.unicode); + emojiModel.emojiUsed(modelData); + } + } + + ScrollBar.vertical: ScrollBar {} + + } + +} diff --git a/src/gui/ErrorBox.qml b/src/gui/ErrorBox.qml new file mode 100644 index 000000000..5b0d102ad --- /dev/null +++ b/src/gui/ErrorBox.qml @@ -0,0 +1,26 @@ +import QtQuick 2.15 + +Item { + id: errorBox + + property var text: "" + + implicitHeight: errorMessage.implicitHeight + 2 * 8 + + Rectangle { + anchors.fill: parent + color: "red" + border.color: "black" + } + + Text { + id: errorMessage + + anchors.fill: parent + anchors.margins: 8 + width: parent.width + color: "white" + wrapMode: Text.WordWrap + text: errorBox.text + } +} diff --git a/src/gui/UserStatusSelector.qml b/src/gui/UserStatusSelector.qml new file mode 100644 index 000000000..c879df442 --- /dev/null +++ b/src/gui/UserStatusSelector.qml @@ -0,0 +1,199 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +import QtQuick 2.6 +import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 +import QtQuick.Window 2.15 +import com.nextcloud.desktopclient 1.0 as NC + +ColumnLayout { + id: rootLayout + spacing: 0 + property NC.UserStatusSelectorModel userStatusSelectorModel + + FontMetrics { + id: metrics + } + + Text { + Layout.topMargin: 16 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.bottomMargin: 8 + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + font.bold: true + text: qsTr("Online status") + } + + GridLayout { + Layout.margins: 8 + Layout.alignment: Qt.AlignTop + columns: 2 + rows: 2 + columnSpacing: 8 + rowSpacing: 8 + + Button { + Layout.fillWidth: true + checked: NC.UserStatus.Online == userStatusSelectorModel.onlineStatus + checkable: true + icon.source: userStatusSelectorModel.onlineIcon + icon.color: "transparent" + text: qsTr("Online") + onClicked: userStatusSelectorModel.setOnlineStatus(NC.UserStatus.Online) + implicitWidth: 100 + } + Button { + Layout.fillWidth: true + checked: NC.UserStatus.Away == userStatusSelectorModel.onlineStatus + checkable: true + icon.source: userStatusSelectorModel.awayIcon + icon.color: "transparent" + text: qsTr("Away") + onClicked: userStatusSelectorModel.setOnlineStatus(NC.UserStatus.Away) + implicitWidth: 100 + + } + Button { + Layout.fillWidth: true + checked: NC.UserStatus.DoNotDisturb == userStatusSelectorModel.onlineStatus + checkable: true + icon.source: userStatusSelectorModel.dndIcon + icon.color: "transparent" + text: qsTr("Do not disturb") + onClicked: userStatusSelectorModel.setOnlineStatus(NC.UserStatus.DoNotDisturb) + implicitWidth: 100 + } + Button { + Layout.fillWidth: true + checked: NC.UserStatus.Invisible == userStatusSelectorModel.onlineStatus + checkable: true + icon.source: userStatusSelectorModel.invisibleIcon + icon.color: "transparent" + text: qsTr("Invisible") + onClicked: userStatusSelectorModel.setOnlineStatus(NC.UserStatus.Invisible) + implicitWidth: 100 + } + } + + Text { + Layout.topMargin: 16 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.bottomMargin: 8 + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + font.bold: true + text: qsTr("Status message") + } + + RowLayout { + Layout.topMargin: 8 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.bottomMargin: 16 + Layout.alignment: Qt.AlignTop + Layout.fillWidth: true + + Button { + Layout.preferredWidth: userStatusMessageTextField.height // metrics.height * 2 + Layout.preferredHeight: userStatusMessageTextField.height // metrics.height * 2 + text: userStatusSelectorModel.userStatusEmoji + onClicked: emojiDialog.open() + } + + Popup { + id: emojiDialog + padding: 0 + margins: 0 + + anchors.centerIn: Overlay.overlay + + EmojiPicker { + id: emojiPicker + + onChosen: { + userStatusSelectorModel.userStatusEmoji = emoji + emojiDialog.close() + } + } + } + + TextField { + id: userStatusMessageTextField + Layout.fillWidth: true + placeholderText: qsTr("What is your Status?") + text: userStatusSelectorModel.userStatusMessage + onEditingFinished: userStatusSelectorModel.setUserStatusMessage(text) + } + } + + Repeater { + model: userStatusSelectorModel.predefinedStatusesCount + + Button { + id: control + Layout.fillWidth: true + flat: !hovered + hoverEnabled: true + text: userStatusSelectorModel.predefinedStatus(index).icon + " <b>" + userStatusSelectorModel.predefinedStatus(index).message + "</b> - " + userStatusSelectorModel.predefinedStatusClearAt(index) + onClicked: userStatusSelectorModel.setPredefinedStatus(index) + } + } + + RowLayout { + Layout.topMargin: 16 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.bottomMargin: 8 + Layout.alignment: Qt.AlignTop + + Text { + text: qsTr("Clear status message after") + } + + ComboBox { + Layout.fillWidth: true + model: userStatusSelectorModel.clearAtValues + displayText: userStatusSelectorModel.clearAt + onActivated: userStatusSelectorModel.setClearAt(index) + } + } + + RowLayout { + Layout.margins: 8 + Layout.alignment: Qt.AlignTop + + Button { + Layout.fillWidth: true + text: qsTr("Clear status message") + onClicked: userStatusSelectorModel.clearUserStatus() + } + Button { + highlighted: true + Layout.fillWidth: true + text: qsTr("Set status message") + onClicked: userStatusSelectorModel.setUserStatus() + } + } + + ErrorBox { + Layout.margins: 8 + Layout.fillWidth: true + + visible: userStatusSelectorModel.errorMessage != "" + text: "<b>Error:</b> " + userStatusSelectorModel.errorMessage + } +} diff --git a/src/gui/UserStatusSelectorDialog.qml b/src/gui/UserStatusSelectorDialog.qml new file mode 100644 index 000000000..624b24f86 --- /dev/null +++ b/src/gui/UserStatusSelectorDialog.qml @@ -0,0 +1,29 @@ +import QtQuick.Window 2.15 + +import com.nextcloud.desktopclient 1.0 as NC + +Window { + id: dialog + + property NC.UserStatusSelectorModel model: NC.UserStatusSelectorModel { + onFinished: { + dialog.close() + } + } + + width: view.implicitWidth + height: view.implicitHeight + minimumWidth: view.implicitWidth + minimumHeight: view.implicitHeight + maximumWidth: view.implicitWidth + maximumHeight: view.implicitHeight + + visible: true + + flags: Qt.Dialog + + UserStatusSelector { + id: view + userStatusSelectorModel: model + } +} diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index bb2ab5273..7521dac79 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -44,7 +44,6 @@ AccountState::AccountState(AccountPtr account) , _waitingForNewCredentials(false) , _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay , _remoteWipe(new RemoteWipe(_account)) - , _userStatus(new UserStatus(this)) , _isDesktopNotificationsAllowed(true) { qRegisterMetaType<AccountState *>("AccountState*"); @@ -127,26 +126,6 @@ void AccountState::setState(State state) emit stateChanged(_state); } -UserStatus::Status AccountState::status() const -{ - return _userStatus->status(); -} - -QString AccountState::statusMessage() const -{ - return _userStatus->message(); -} - -QUrl AccountState::statusIcon() const -{ - return _userStatus->icon(); -} - -QString AccountState::statusEmoji() const -{ - return _userStatus->emoji(); -} - QString AccountState::stateString(State state) { switch (state) { @@ -462,12 +441,6 @@ void AccountState::fetchNavigationApps(){ job->getNavigationApps(); } -void AccountState::fetchUserStatus() -{ - connect(_userStatus, &UserStatus::fetchUserStatusFinished, this, &AccountState::statusChanged); - _userStatus->fetchUserStatus(_account); -} - void AccountState::slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode){ if(statusCode == 200){ qCDebug(lcAccountState) << "New navigation apps ETag Response Header received " << value; diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index 74e41ad6a..7946e2f96 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -21,7 +21,7 @@ #include <QPointer> #include "connectionvalidator.h" #include "creds/abstractcredentials.h" -#include "userstatus.h" + #include <memory> class QSettings; @@ -162,23 +162,6 @@ public: ///Asks for user credentials void handleInvalidCredentials(); - /** Returns the user status (Online, Dnd, Away, Offline, Invisible) - * https://gist.github.com/georgehrke/55a0412007f13be1551d1f9436a39675 - */ - UserStatus::Status status() const; - - /** Returns the user status Message (text) - */ - QString statusMessage() const; - - /** Returns the user status icon url - */ - QUrl statusIcon() const; - - /** Returns the user status emoji - */ - QString statusEmoji() const; - /** Returns the notifications status retrieved by the notificatons endpoint * https://github.com/nextcloud/desktop/issues/2318#issuecomment-680698429 */ @@ -188,10 +171,6 @@ public: */ void setDesktopNotificationsAllowed(bool isAllowed); - /** Fetch the user status (status, icon, message) - */ - void fetchUserStatus(); - public slots: /// Triggers a ping to the server to update state and /// connection status and errors. @@ -256,7 +235,6 @@ private: */ AccountAppList _apps; - UserStatus *_userStatus; bool _isDesktopNotificationsAllowed; }; diff --git a/src/gui/emojimodel.cpp b/src/gui/emojimodel.cpp new file mode 100644 index 000000000..e6b281db1 --- /dev/null +++ b/src/gui/emojimodel.cpp @@ -0,0 +1,1571 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#include <QtGlobal> +#include <memory> + +#include "emojimodel.h" + +namespace OCC { + +QVariant EmojiCategoriesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return {}; + } + + switch (role) { + case Roles::EmojiRole: + return categories[index.row()].emoji; + + case Roles::LabelRole: + return categories[index.row()].label; + } + + return {}; +} + +int EmojiCategoriesModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return static_cast<int>(categories.size()); +} + +QHash<int, QByteArray> EmojiCategoriesModel::roleNames() const +{ + QHash<int, QByteArray> roles; + roles[Roles::EmojiRole] = "emoji"; + roles[Roles::LabelRole] = "label"; + return roles; +} + +const std::vector<EmojiCategoriesModel::Category> EmojiCategoriesModel::categories = { + { "⌛️", "history" }, + { "😏", "people" }, + { "🌲", "nature" }, + { "🍛", "food" }, + { "🚁", "activity" }, + { "🚅", "travel" }, + { "💡", "objects" }, + { "🔣", "symbols" }, + { "🏁", "flags" }, +}; + +QVariantList EmojiModel::history() const +{ + return _settings.value("Editor/emojis", QVariantList()).toList(); +} + +void EmojiModel::setCategory(const QString &category) +{ + if (_category == category) { + return; + } + _category = category; + emit modelChanged(); +} + +QAbstractListModel *EmojiModel::emojiCategoriesModel() +{ + return &_emojiCategoriesModel; +} + +QVariantList EmojiModel::model() const +{ + if (_category == "history") { + return history(); + } else if (_category == "people") { + return people; + } else if (_category == "nature") { + return nature; + } else if (_category == "food") { + return food; + } else if (_category == "activity") { + return activity; + } else if (_category == "travel") { + return travel; + } else if (_category == "objects") { + return objects; + } else if (_category == "symbols") { + return symbols; + } else if (_category == "flags") { + return flags; + } + return history(); +} + +void EmojiModel::emojiUsed(const QVariant &modelData) +{ + auto historyEmojis = history(); + + auto historyEmojisIter = historyEmojis.begin(); + while (historyEmojisIter != historyEmojis.end()) { + if ((*historyEmojisIter).value<Emoji>().unicode == modelData.value<Emoji>().unicode) { + historyEmojisIter = historyEmojis.erase(historyEmojisIter); + } else { + historyEmojisIter++; + } + } + + historyEmojis.push_front(modelData); + _settings.setValue("Editor/emojis", historyEmojis); + + emit historyChanged(); +} + +const QVariantList EmojiModel::people = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x80"), ":grinning:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x81"), ":grin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x82"), ":joy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa3"), ":rofl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x83"), ":smiley:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x84"), ":smile:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x85"), ":sweat_smile:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x86"), ":laughing:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x89"), ":wink:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x8a"), ":blush:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x8b"), ":yum:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x8e"), ":sunglasses:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x8d"), ":heart_eyes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x98"), ":kissing_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x97"), ":kissing:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x99"), ":kissing_smiling_eyes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x9a"), ":kissing_closed_eyes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xba"), ":relaxed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x82"), ":slight_smile:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x97"), ":hugging:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x94"), ":thinking:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x90"), ":neutral_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x91"), ":expressionless:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb6"), ":no_mouth:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x84"), ":rolling_eyes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x8f"), ":smirk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa3"), ":persevere:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa5"), ":disappointed_relieved:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xae"), ":open_mouth:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x90"), ":zipper_mouth:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xaf"), ":hushed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xaa"), ":sleepy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xab"), ":tired_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb4"), ":sleeping:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x8c"), ":relieved:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x93"), ":nerd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x9b"), ":stuck_out_tongue:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x9c"), ":stuck_out_tongue_winking_eye:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x9d"), ":stuck_out_tongue_closed_eyes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa4"), ":drooling_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x92"), ":unamused:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x93"), ":sweat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x94"), ":pensive:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x95"), ":confused:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x83"), ":upside_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x91"), ":money_mouth:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb2"), ":astonished:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xb9"), ":frowning2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x81"), ":slight_frown:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x96"), ":confounded:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x9e"), ":disappointed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x9f"), ":worried:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa4"), ":triumph:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa2"), ":cry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xad"), ":sob:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa6"), ":frowning:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa7"), ":anguished:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa8"), ":fearful:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa9"), ":weary:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xac"), ":grimacing:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb0"), ":cold_sweat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb1"), ":scream:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb3"), ":flushed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb5"), ":dizzy_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa1"), ":rage:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xa0"), ":angry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x87"), ":innocent:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa0"), ":cowboy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa1"), ":clown:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa5"), ":lying_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb7"), ":mask:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x92"), ":thermometer_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x95"), ":head_bandage:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa2"), ":nauseated_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa7"), ":sneezing_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\x88"), ":smiling_imp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xbf"), ":imp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb9"), ":japanese_ogre:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xba"), ":japanese_goblin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x80"), ":skull:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xbb"), ":ghost:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xbd"), ":alien:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x96"), ":robot:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa9"), ":poop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xba"), ":smiley_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb8"), ":smile_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xb9"), ":joy_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xbb"), ":heart_eyes_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xbc"), ":smirk_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xbd"), ":kissing_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x80"), ":scream_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xbf"), ":crying_cat_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x98\xbe"), ":pouting_cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa6"), ":boy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa7"), ":girl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa8"), ":man:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa9"), ":woman:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb4"), ":older_man:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb5"), ":older_woman:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb6"), ":baby:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xbc"), ":angel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xae"), ":cop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb5"), ":spy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x82"), ":guardsman:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb7"), ":construction_worker:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb3"), ":man_with_turban:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb1"), ":person_with_blond_hair:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x85"), ":santa:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb6"), ":mrs_claus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb8"), ":princess:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb4"), ":prince:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb0"), ":bride_with_veil:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb5"), ":man_in_tuxedo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb0"), ":pregnant_woman:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xb2"), ":man_with_gua_pi_mao:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x8d"), ":person_frowning:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x8e"), ":person_with_pouting_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x85"), ":no_good:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x86"), ":ok_woman:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x81"), ":information_desk_person:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x8b"), ":raising_hand:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x87"), ":bow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xa6"), ":face_palm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb7"), ":shrug:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x86"), ":massage:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x87"), ":haircut:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb6"), ":walking:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x83"), ":runner:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x83"), ":dancer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xba"), ":man_dancing:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xaf"), ":dancers:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xa3"), ":speaking_head:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa4"), ":bust_in_silhouette:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa5"), ":busts_in_silhouette:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xab"), ":couple:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xac"), ":two_men_holding_hands:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xad"), ":two_women_holding_hands:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x8f"), ":couplekiss:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x91"), ":couple_with_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xaa"), ":family:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xaa"), ":muscle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb3"), ":selfie:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x88"), ":point_left:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x89"), ":point_right:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x9d"), ":point_up:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x86"), ":point_up_2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x95"), ":middle_finger:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x87"), ":point_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x8c"), ":v:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x9e"), ":fingers_crossed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x96"), ":vulcan:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x98"), ":metal:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x99"), ":call_me:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x90"), ":hand_splayed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x8b"), ":raised_hand:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x8c"), ":ok_hand:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x8d"), ":thumbsup:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x8e"), ":thumbsdown:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x8a"), ":fist:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x8a"), ":punch:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x9b"), ":left_facing_fist:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x9c"), ":right_facing_fist:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x9a"), ":raised_back_of_hand:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x8b"), ":wave:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x8f"), ":clap:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x8d"), ":writing_hand:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x90"), ":open_hands:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x8c"), ":raised_hands:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x8f"), ":pray:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\x9d"), ":handshake:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x85"), ":nail_care:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x82"), ":ear:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x83"), ":nose:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa3"), ":footprints:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x80"), ":eyes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x81"), ":eye:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x85"), ":tongue:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x84"), ":lips:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x8b"), ":kiss:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa4"), ":zzz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x93"), ":eyeglasses:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb6"), ":dark_sunglasses:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x94"), ":necktie:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x95"), ":shirt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x96"), ":jeans:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x97"), ":dress:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x98"), ":kimono:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x99"), ":bikini:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x9a"), ":womans_clothes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x9b"), ":purse:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x9c"), ":handbag:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x9d"), ":pouch:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x92"), ":school_satchel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x9e"), ":mans_shoe:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x9f"), ":athletic_shoe:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa0"), ":high_heel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa1"), ":sandal:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xa2"), ":boot:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x91"), ":crown:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x92"), ":womans_hat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa9"), ":tophat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x93"), ":mortar_board:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x91"), ":helmet_with_cross:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x84"), ":lipstick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x8d"), ":ring:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x82"), ":closed_umbrella:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xbc"), ":briefcase:" }), +}; + +const QVariantList EmojiModel::nature = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x88"), ":see_no_evil:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x89"), ":hear_no_evil:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x99\x8a"), ":speak_no_evil:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa6"), ":sweat_drops:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa8"), ":dash:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb5"), ":monkey_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x92"), ":monkey:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x8d"), ":gorilla:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb6"), ":dog:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x95"), ":dog2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa9"), ":poodle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xba"), ":wolf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x8a"), ":fox:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb1"), ":cat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x88"), ":cat2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x81"), ":lion_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xaf"), ":tiger:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x85"), ":tiger2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x86"), ":leopard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb4"), ":horse:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x8e"), ":racehorse:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x8c"), ":deer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x84"), ":unicorn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xae"), ":cow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x82"), ":ox:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x83"), ":water_buffalo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x84"), ":cow2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb7"), ":pig:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x96"), ":pig2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x97"), ":boar:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xbd"), ":pig_nose:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x8f"), ":ram:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x91"), ":sheep:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x90"), ":goat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xaa"), ":dromedary_camel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xab"), ":camel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x98"), ":elephant:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x8f"), ":rhino:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xad"), ":mouse:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x81"), ":mouse2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x80"), ":rat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb9"), ":hamster:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb0"), ":rabbit:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x87"), ":rabbit2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xbf"), ":chipmunk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x87"), ":bat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xbb"), ":bear:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa8"), ":koala:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xbc"), ":panda_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xbe"), ":feet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x83"), ":turkey:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x94"), ":chicken:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x93"), ":rooster:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa3"), ":hatching_chick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa4"), ":baby_chick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa5"), ":hatched_chick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa6"), ":bird:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa7"), ":penguin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x8a"), ":dove:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x85"), ":eagle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x86"), ":duck:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x89"), ":owl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb8"), ":frog:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x8a"), ":crocodile:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa2"), ":turtle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x8e"), ":lizard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x8d"), ":snake:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb2"), ":dragon_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x89"), ":dragon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xb3"), ":whale:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x8b"), ":whale2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xac"), ":dolphin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x9f"), ":fish:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa0"), ":tropical_fish:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\xa1"), ":blowfish:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x88"), ":shark:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x99"), ":octopus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x9a"), ":shell:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x80"), ":crab:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x90"), ":shrimp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x91"), ":squid:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x8b"), ":butterfly:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x8c"), ":snail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x9b"), ":bug:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x9c"), ":ant:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x9d"), ":bee:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x90\x9e"), ":beetle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb7"), ":spider:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb8"), ":spider_web:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa6\x82"), ":scorpion:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x90"), ":bouquet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb8"), ":cherry_blossom:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb5"), ":rosette:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb9"), ":rose:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x80"), ":wilted_rose:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xba"), ":hibiscus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xbb"), ":sunflower:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xbc"), ":blossom:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb7"), ":tulip:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb1"), ":seedling:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb2"), ":evergreen_tree:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb3"), ":deciduous_tree:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb4"), ":palm_tree:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb5"), ":cactus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xbe"), ":ear_of_rice:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xbf"), ":herb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x98"), ":shamrock:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x80"), ":four_leaf_clover:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x81"), ":maple_leaf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x82"), ":fallen_leaf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x83"), ":leaves:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x84"), ":mushroom:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb0"), ":chestnut:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x8d"), ":earth_africa:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x8e"), ":earth_americas:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x8f"), ":earth_asia:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x91"), ":new_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x92"), ":waxing_crescent_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x93"), ":first_quarter_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x94"), ":waxing_gibbous_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x95"), ":full_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x96"), ":waning_gibbous_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x97"), ":last_quarter_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x98"), ":waning_crescent_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x99"), ":crescent_moon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x9a"), ":new_moon_with_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x9b"), ":first_quarter_moon_with_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x9c"), ":last_quarter_moon_with_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x80"), ":sunny:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x9d"), ":full_moon_with_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x9e"), ":sun_with_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xad\x90"), ":star:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x9f"), ":star2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x81"), ":cloud:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x85"), ":partly_sunny:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x88"), ":thunder_cloud_rain:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa4"), ":white_sun_small_cloud:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa5"), ":white_sun_cloud:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa6"), ":white_sun_rain_cloud:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa7"), ":cloud_rain:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa8"), ":cloud_snow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa9"), ":cloud_lightning:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xaa"), ":cloud_tornado:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xab"), ":fog:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xac"), ":wind_blowing_face:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x82"), ":umbrella2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x94"), ":umbrella:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xa1"), ":zap:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x84"), ":snowflake:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x83"), ":snowman2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x84"), ":snowman:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x84"), ":comet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa5"), ":fire:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa7"), ":droplet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x8a"), ":ocean:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x83"), ":jack_o_lantern:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x84"), ":christmas_tree:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\xa8"), ":sparkles:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x8b"), ":tanabata_tree:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x8d"), ":bamboo:" }), +}; + +const QVariantList EmojiModel::food = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x87"), ":grapes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x88"), ":melon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x89"), ":watermelon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x8a"), ":tangerine:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x8b"), ":lemon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x8c"), ":banana:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x8d"), ":pineapple:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x8e"), ":apple:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x8f"), ":green_apple:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x90"), ":pear:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x91"), ":peach:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x92"), ":cherries:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x93"), ":strawberry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x9d"), ":kiwi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x85"), ":tomato:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x91"), ":avocado:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x86"), ":eggplant:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x94"), ":potato:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x95"), ":carrot:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xbd"), ":corn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xb6"), ":hot_pepper:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x92"), ":cucumber:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x9c"), ":peanuts:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x9e"), ":bread:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x90"), ":croissant:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x96"), ":french_bread:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x9e"), ":pancakes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa7\x80"), ":cheese:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x96"), ":meat_on_bone:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x97"), ":poultry_leg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x93"), ":bacon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x94"), ":hamburger:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x9f"), ":fries:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x95"), ":pizza:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xad"), ":hotdog:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xae"), ":taco:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xaf"), ":burrito:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x99"), ":stuffed_flatbread:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x9a"), ":egg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb3"), ":cooking:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x98"), ":shallow_pan_of_food:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb2"), ":stew:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x97"), ":salad:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xbf"), ":popcorn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb1"), ":bento:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x98"), ":rice_cracker:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x99"), ":rice_ball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x9a"), ":rice:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x9b"), ":curry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x9c"), ":ramen:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\x9d"), ":spaghetti:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa0"), ":sweet_potato:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa2"), ":oden:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa3"), ":sushi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa4"), ":fried_shrimp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa5"), ":fish_cake:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa1"), ":dango:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa6"), ":icecream:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa7"), ":shaved_ice:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa8"), ":ice_cream:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xa9"), ":doughnut:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xaa"), ":cookie:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x82"), ":birthday:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb0"), ":cake:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xab"), ":chocolate_bar:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xac"), ":candy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xad"), ":lollipop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xae"), ":custard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xaf"), ":honey_pot:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xbc"), ":baby_bottle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x9b"), ":milk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x95"), ":coffee:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb5"), ":tea:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb6"), ":sake:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xbe"), ":champagne:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb7"), ":wine_glass:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb8"), ":cocktail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb9"), ":tropical_drink:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xba"), ":beer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xbb"), ":beers:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x82"), ":champagne_glass:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x83"), ":tumbler_glass:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xbd"), ":fork_knife_plate:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8d\xb4"), ":fork_and_knife:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x84"), ":spoon:" }), +}; + +const QVariantList EmojiModel::activity = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\xbe"), ":space_invader:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb4"), ":levitate:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xba"), ":fencer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x87"), ":horse_racing:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb"), ":horse_racing_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc"), ":horse_racing_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd"), ":horse_racing_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe"), ":horse_racing_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf"), ":horse_racing_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb7"), ":skier:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x82"), ":snowboarder:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8c"), ":golfer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x84"), ":surfer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb"), ":surfer_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc"), ":surfer_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd"), ":surfer_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe"), ":surfer_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf"), ":surfer_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa3"), ":rowboat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb"), ":rowboat_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc"), ":rowboat_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd"), ":rowboat_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe"), ":rowboat_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf"), ":rowboat_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8a"), ":swimmer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb"), ":swimmer_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc"), ":swimmer_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd"), ":swimmer_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe"), ":swimmer_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf"), ":swimmer_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb9"), ":basketball_player:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbb"), ":basketball_player_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbc"), ":basketball_player_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbd"), ":basketball_player_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbe"), ":basketball_player_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbf"), ":basketball_player_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8b"), ":lifter:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb"), ":lifter_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc"), ":lifter_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd"), ":lifter_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe"), ":lifter_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf"), ":lifter_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb4"), ":bicyclist:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb"), ":bicyclist_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc"), ":bicyclist_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd"), ":bicyclist_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe"), ":bicyclist_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf"), ":bicyclist_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb5"), ":mountain_bicyclist:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb"), ":mountain_bicyclist_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc"), ":mountain_bicyclist_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd"), ":mountain_bicyclist_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe"), ":mountain_bicyclist_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf"), ":mountain_bicyclist_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb8"), ":cartwheel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb"), ":cartwheel_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc"), ":cartwheel_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd"), ":cartwheel_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe"), ":cartwheel_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf"), ":cartwheel_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbc"), ":wrestlers:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb"), ":wrestlers_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc"), ":wrestlers_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd"), ":wrestlers_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe"), ":wrestlers_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf"), ":wrestlers_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbd"), ":water_polo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb"), ":water_polo_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc"), ":water_polo_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd"), ":water_polo_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe"), ":water_polo_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf"), ":water_polo_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbe"), ":handball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb"), ":handball_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc"), ":handball_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd"), ":handball_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe"), ":handball_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf"), ":handball_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb9"), ":juggling:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb"), ":juggling_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc"), ":juggling_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd"), ":juggling_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe"), ":juggling_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf"), ":juggling_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xaa"), ":circus_tent:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xad"), ":performing_arts:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa8"), ":art:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb0"), ":slot_machine:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x80"), ":bath:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb"), ":bath_tone1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc"), ":bath_tone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd"), ":bath_tone3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe"), ":bath_tone4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf"), ":bath_tone5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x97"), ":reminder_ribbon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x9f"), ":tickets:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xab"), ":ticket:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x96"), ":military_medal:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x86"), ":trophy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x85"), ":medal:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x87"), ":first_place:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x88"), ":second_place:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x89"), ":third_place:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xbd"), ":soccer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xbe"), ":baseball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x80"), ":basketball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x90"), ":volleyball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x88"), ":football:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x89"), ":rugby_football:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xbe"), ":tennis:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb1"), ":8ball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb3"), ":bowling:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8f"), ":cricket:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x91"), ":field_hockey:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x92"), ":hockey:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x93"), ":ping_pong:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb8"), ":badminton:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x8a"), ":boxing_glove:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x8b"), ":martial_arts_uniform:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x85"), ":goal:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xaf"), ":dart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb3"), ":golf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb8"), ":ice_skate:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa3"), ":fishing_pole_and_fish:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xbd"), ":running_shirt_with_sash:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xbf"), ":ski:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xae"), ":video_game:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb2"), ":game_die:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xbc"), ":musical_score:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa4"), ":microphone:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa7"), ":headphones:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb7"), ":saxophone:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb8"), ":guitar:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb9"), ":musical_keyboard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xba"), ":trumpet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xbb"), ":violin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\xa5\x81"), ":drum:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xac"), ":clapper:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb9"), ":bow_and_arrow:" }), +}; + +const QVariantList EmojiModel::travel = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8e"), ":race_car:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x8d"), ":motorcycle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xbe"), ":japan:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x94"), ":mountain_snow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb0"), ":mountain:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x8b"), ":volcano:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xbb"), ":mount_fuji:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x95"), ":camping:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x96"), ":beach:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x9c"), ":desert:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x9d"), ":island:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x9e"), ":park:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x9f"), ":stadium:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x9b"), ":classical_building:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x97"), ":construction_site:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x98"), ":homes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x99"), ":cityscape:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x9a"), ":house_abandoned:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa0"), ":house:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa1"), ":house_with_garden:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa2"), ":office:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa3"), ":post_office:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa4"), ":european_post_office:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa5"), ":hospital:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa6"), ":bank:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa8"), ":hotel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa9"), ":love_hotel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xaa"), ":convenience_store:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xab"), ":school:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xac"), ":department_store:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xad"), ":factory:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xaf"), ":japanese_castle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb0"), ":european_castle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x92"), ":wedding:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xbc"), ":tokyo_tower:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xbd"), ":statue_of_liberty:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xaa"), ":church:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x8c"), ":mosque:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x8d"), ":synagogue:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xa9"), ":shinto_shrine:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x8b"), ":kaaba:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb2"), ":fountain:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xba"), ":tent:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x81"), ":foggy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x83"), ":night_with_stars:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x84"), ":sunrise_over_mountains:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x85"), ":sunrise:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x86"), ":city_dusk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x87"), ":city_sunset:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x89"), ":bridge_at_night:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x8c"), ":milky_way:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa0"), ":carousel_horse:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa1"), ":ferris_wheel:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa2"), ":roller_coaster:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x82"), ":steam_locomotive:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x83"), ":railway_car:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x84"), ":bullettrain_side:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x85"), ":bullettrain_front:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x86"), ":train2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x87"), ":metro:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x88"), ":light_rail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x89"), ":station:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x8a"), ":tram:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x9d"), ":monorail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x9e"), ":mountain_railway:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x8b"), ":train:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x8c"), ":bus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x8d"), ":oncoming_bus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x8e"), ":trolleybus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x90"), ":minibus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x91"), ":ambulance:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x92"), ":fire_engine:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x93"), ":police_car:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x94"), ":oncoming_police_car:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x95"), ":taxi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x96"), ":oncoming_taxi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x97"), ":red_car:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x98"), ":oncoming_automobile:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x99"), ":blue_car:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x9a"), ":truck:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x9b"), ":articulated_lorry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x9c"), ":tractor:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb2"), ":bike:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xb4"), ":scooter:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xb5"), ":motor_scooter:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x8f"), ":busstop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa3"), ":motorway:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa4"), ":railway_track:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xbd"), ":fuelpump:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa8"), ":rotating_light:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa5"), ":traffic_light:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa6"), ":vertical_traffic_light:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa7"), ":construction:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x93"), ":anchor:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb5"), ":sailboat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xb6"), ":canoe:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa4"), ":speedboat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xb3"), ":cruise_ship:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb4"), ":ferry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa5"), ":motorboat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa2"), ":ship:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x88"), ":airplane:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa9"), ":airplane_small:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xab"), ":airplane_departure:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xac"), ":airplane_arriving:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xba"), ":seat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x81"), ":helicopter:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x9f"), ":suspension_railway:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa0"), ":mountain_cableway:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa1"), ":aerial_tramway:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\x80"), ":rocket:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xb0"), ":satellite_orbital:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa0"), ":stars:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x88"), ":rainbow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x86"), ":fireworks:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x87"), ":sparkler:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x91"), ":rice_scene:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\x81"), ":checkered_flag:" }), +}; + +const QVariantList EmojiModel::objects = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xa0"), ":skull_crossbones:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x8c"), ":love_letter:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa3"), ":bomb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb3"), ":hole:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x8d"), ":shopping_bags:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xbf"), ":prayer_beads:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x8e"), ":gem:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xaa"), ":knife:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xba"), ":amphora:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xba"), ":map:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x88"), ":barber:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\xbc"), ":frame_photo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x8e"), ":bellhop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xaa"), ":door:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x8c"), ":sleeping_accommodation:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x8f"), ":bed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x8b"), ":couch:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xbd"), ":toilet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xbf"), ":shower:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x81"), ":bathtub:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8c\x9b"), ":hourglass:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xb3"), ":hourglass_flowing_sand:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8c\x9a"), ":watch:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xb0"), ":alarm_clock:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xb1"), ":stopwatch:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xb2"), ":timer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb0"), ":clock:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\xa1"), ":thermometer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\xb1"), ":beach_umbrella:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x88"), ":balloon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x89"), ":tada:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x8a"), ":confetti_ball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x8e"), ":dolls:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x8f"), ":flags:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x90"), ":wind_chime:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x80"), ":ribbon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x81"), ":gift:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xb9"), ":joystick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xaf"), ":postal_horn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x99"), ":microphone2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x9a"), ":level_slider:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x9b"), ":control_knobs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xbb"), ":radio:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb1"), ":iphone:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb2"), ":calling:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x8e"), ":telephone:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x9e"), ":telephone_receiver:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x9f"), ":pager:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa0"), ":fax:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x8b"), ":battery:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x8c"), ":electric_plug:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xbb"), ":computer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\xa5"), ":desktop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\xa8"), ":printer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8c\xa8"), ":keyboard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\xb1"), ":mouse_three_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\xb2"), ":trackball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xbd"), ":minidisc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xbe"), ":floppy_disk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xbf"), ":cd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x80"), ":dvd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa5"), ":movie_camera:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x9e"), ":film_frames:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xbd"), ":projector:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xba"), ":tv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb7"), ":camera:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb8"), ":camera_with_flash:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb9"), ":video_camera:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xbc"), ":vhs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x8d"), ":mag:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x8e"), ":mag_right:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xac"), ":microscope:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xad"), ":telescope:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa1"), ":satellite:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xaf"), ":candle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa1"), ":bulb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa6"), ":flashlight:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xae"), ":izakaya_lantern:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x94"), ":notebook_with_decorative_cover:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x95"), ":closed_book:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x96"), ":book:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x97"), ":green_book:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x98"), ":blue_book:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x99"), ":orange_book:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x9a"), ":books:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x93"), ":notebook:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x92"), ":ledger:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x83"), ":page_with_curl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x9c"), ":scroll:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x84"), ":page_facing_up:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb0"), ":newspaper:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x9e"), ":newspaper2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x91"), ":bookmark_tabs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x96"), ":bookmark:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb7"), ":label:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb0"), ":moneybag:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb4"), ":yen:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb5"), ":dollar:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb6"), ":euro:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb7"), ":pound:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb8"), ":money_with_wings:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb3"), ":credit_card:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x89"), ":envelope:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa7"), ":e-mail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa8"), ":incoming_envelope:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa9"), ":envelope_with_arrow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa4"), ":outbox_tray:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa5"), ":inbox_tray:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa6"), ":package:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xab"), ":mailbox:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xaa"), ":mailbox_closed:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xac"), ":mailbox_with_mail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xad"), ":mailbox_with_no_mail:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xae"), ":postbox:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xb3"), ":ballot_box:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x8f"), ":pencil2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x92"), ":black_nib:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x8b"), ":pen_fountain:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x8a"), ":pen_ballpoint:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x8c"), ":paintbrush:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x8d"), ":crayon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x9d"), ":pencil:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x81"), ":file_folder:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x82"), ":open_file_folder:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x82"), ":dividers:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x85"), ":date:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x86"), ":calendar:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x92"), ":notepad_spiral:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x93"), ":calendar_spiral:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x87"), ":card_index:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x88"), ":chart_with_upwards_trend:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x89"), ":chart_with_downwards_trend:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x8a"), ":bar_chart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x8b"), ":clipboard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x8c"), ":pushpin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x8d"), ":round_pushpin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x8e"), ":paperclip:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\x87"), ":paperclips:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x8f"), ":straight_ruler:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x90"), ":triangular_ruler:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x82"), ":scissors:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x83"), ":card_box:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x84"), ":file_cabinet:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x91"), ":wastebasket:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x92"), ":lock:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x93"), ":unlock:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x8f"), ":lock_with_ink_pen:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x90"), ":closed_lock_with_key:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x91"), ":key:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x9d"), ":key2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa8"), ":hammer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x8f"), ":pick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x92"), ":hammer_pick:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa0"), ":tools:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xa1"), ":dagger:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x94"), ":crossed_swords:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xab"), ":gun:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa1"), ":shield:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa7"), ":wrench:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa9"), ":nut_and_bolt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x99"), ":gear:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\x9c"), ":compression:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x97"), ":alembic:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x96"), ":scales:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x97"), ":link:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x93"), ":chains:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x89"), ":syringe:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x8a"), ":pill:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xac"), ":smoking:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xb0"), ":coffin:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xb1"), ":urn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xbf"), ":moyai:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\xa2"), ":oil:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xae"), ":crystal_ball:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x92"), ":shopping_cart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xa9"), ":triangular_flag_on_post:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\x8c"), ":crossed_flags:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb4"), ":flag_black:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb3"), ":flag_white:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"), ":rainbow_flag:" }), +}; + +const QVariantList EmojiModel::symbols = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x91\x81\xf0\x9f\x97\xa8"), ":eye_in_speech_bubble:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x98"), ":cupid:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\xa4"), ":heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x93"), ":heartbeat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x94"), ":broken_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x95"), ":two_hearts:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x96"), ":sparkling_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x97"), ":heartpulse:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x99"), ":blue_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x9a"), ":green_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x9b"), ":yellow_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x9c"), ":purple_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x96\xa4"), ":black_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x9d"), ":gift_heart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x9e"), ":revolving_hearts:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\x9f"), ":heart_decoration:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\xa3"), ":heart_exclamation:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa2"), ":anger:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa5"), ":boom:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xab"), ":dizzy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xac"), ":speech_balloon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xa8"), ":speech_left:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x97\xaf"), ":anger_right:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xad"), ":thought_balloon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xae"), ":white_flower:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x90"), ":globe_with_meridians:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xa8"), ":hotsprings:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x91"), ":octagonal_sign:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x9b"), ":clock12:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa7"), ":clock1230:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x90"), ":clock1:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x9c"), ":clock130:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x91"), ":clock2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x9d"), ":clock230:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x92"), ":clock3:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x9e"), ":clock330:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x93"), ":clock4:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x9f"), ":clock430:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x94"), ":clock5:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa0"), ":clock530:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x95"), ":clock6:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa1"), ":clock630:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x96"), ":clock7:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa2"), ":clock730:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x97"), ":clock8:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa3"), ":clock830:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x98"), ":clock9:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa4"), ":clock930:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x99"), ":clock10:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa5"), ":clock1030:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x9a"), ":clock11:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\xa6"), ":clock1130:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8c\x80"), ":cyclone:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xa0"), ":spades:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xa5"), ":hearts:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xa6"), ":diamonds:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xa3"), ":clubs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x83\x8f"), ":black_joker:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x80\x84"), ":mahjong:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb4"), ":flower_playing_cards:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x87"), ":mute:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x88"), ":speaker:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x89"), ":sound:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x8a"), ":loud_sound:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa2"), ":loudspeaker:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xa3"), ":mega:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x94"), ":bell:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x95"), ":no_bell:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb5"), ":musical_note:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xb6"), ":notes:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb9"), ":chart:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb1"), ":currency_exchange:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xb2"), ":heavy_dollar_sign:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8f\xa7"), ":atm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xae"), ":put_litter_in_its_place:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb0"), ":potable_water:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xbf"), ":wheelchair:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb9"), ":mens:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xba"), ":womens:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xbb"), ":restroom:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xbc"), ":baby_symbol:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xbe"), ":wc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x82"), ":passport_control:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x83"), ":customs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x84"), ":baggage_claim:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x85"), ":left_luggage:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xa0"), ":warning:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb8"), ":children_crossing:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x94"), ":no_entry:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xab"), ":no_entry_sign:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb3"), ":no_bicycles:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xad"), ":no_smoking:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xaf"), ":do_not_litter:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb1"), ":non-potable_water:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9a\xb7"), ":no_pedestrians:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb5"), ":no_mobile_phones:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x9e"), ":underage:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xa2"), ":radioactive:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xa3"), ":biohazard:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xac\x86"), ":arrow_up:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\x97"), ":arrow_upper_right:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9e\xa1"), ":arrow_right:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\x98"), ":arrow_lower_right:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xac\x87"), ":arrow_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\x99"), ":arrow_lower_left:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xac\x85"), ":arrow_left:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\x96"), ":arrow_upper_left:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\x95"), ":arrow_up_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\x94"), ":left_right_arrow:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\xa9"), ":leftwards_arrow_with_hook:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x86\xaa"), ":arrow_right_hook:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xa4\xb4"), ":arrow_heading_up:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xa4\xb5"), ":arrow_heading_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x83"), ":arrows_clockwise:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x84"), ":arrows_counterclockwise:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x99"), ":back:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x9a"), ":end:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x9b"), ":on:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x9c"), ":soon:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x9d"), ":top:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x9b\x90"), ":place_of_worship:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x9b"), ":atom:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x89"), ":om_symbol:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\xa1"), ":star_of_david:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xb8"), ":wheel_of_dharma:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xaf"), ":yin_yang:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x9d"), ":cross:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xa6"), ":orthodox_cross:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xaa"), ":star_and_crescent:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\xae"), ":peace:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x95\x8e"), ":menorah:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xaf"), ":six_pointed_star:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x88"), ":aries:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x89"), ":taurus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x8a"), ":gemini:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x8b"), ":cancer:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x8c"), ":leo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x8d"), ":virgo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x8e"), ":libra:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x8f"), ":scorpius:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x90"), ":sagittarius:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x91"), ":capricorn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x92"), ":aquarius:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\x93"), ":pisces:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9b\x8e"), ":ophiuchus:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x80"), ":twisted_rightwards_arrows:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x81"), ":repeat:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x82"), ":repeat_one:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x96\xb6"), ":arrow_forward:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xa9"), ":fast_forward:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xad"), ":track_next:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xaf"), ":play_pause:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x97\x80"), ":arrow_backward:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xaa"), ":rewind:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xae"), ":track_previous:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xbc"), ":arrow_up_small:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xab"), ":arrow_double_up:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xbd"), ":arrow_down_small:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xac"), ":arrow_double_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xb8"), ":pause_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xb9"), ":stop_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\xba"), ":record_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x8f\x8f"), ":eject:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x8e\xa6"), ":cinema:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x85"), ":low_brightness:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x86"), ":high_brightness:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb6"), ":signal_strength:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb3"), ":vibration_mode:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\xb4"), ":mobile_phone_off:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x99\xbb"), ":recycle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x93\x9b"), ":name_badge:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\x9c"), ":fleur-de-lis:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb0"), ":beginner:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb1"), ":trident:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xad\x95"), ":o:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x85"), ":white_check_mark:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x98\x91"), ":ballot_box_with_check:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x94"), ":heavy_check_mark:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\x96"), ":heavy_multiplication_x:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x8c"), ":x:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x8e"), ":negative_squared_cross_mark:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9e\x95"), ":heavy_plus_sign:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9e\x96"), ":heavy_minus_sign:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9e\x97"), ":heavy_division_sign:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9e\xb0"), ":curly_loop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9e\xbf"), ":loop:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe3\x80\xbd"), ":part_alternation_mark:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\xb3"), ":eight_spoked_asterisk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9c\xb4"), ":eight_pointed_black_star:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x87"), ":sparkle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x80\xbc"), ":bangbang:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x81\x89"), ":interrobang:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x93"), ":question:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x94"), ":grey_question:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x95"), ":grey_exclamation:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9d\x97"), ":exclamation:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe3\x80\xb0"), ":wavy_dash:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xc2\xa9"), ":copyright:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xc2\xae"), ":registered:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x84\xa2"), ":tm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("#\xe2\x83\xa3"), ":hash:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("*\xe2\x83\xa3"), ":asterisk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("0\xe2\x83\xa3"), ":zero:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("1\xe2\x83\xa3"), ":one:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("2\xe2\x83\xa3"), ":two:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("3\xe2\x83\xa3"), ":three:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("4\xe2\x83\xa3"), ":four:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("5\xe2\x83\xa3"), ":five:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("6\xe2\x83\xa3"), ":six:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("7\xe2\x83\xa3"), ":seven:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("8\xe2\x83\xa3"), ":eight:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("9\xe2\x83\xa3"), ":nine:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x9f"), ":keycap_ten:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xaf"), ":100:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa0"), ":capital_abcd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa1"), ":abcd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa2"), ":1234:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa3"), ":symbols:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xa4"), ":abc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x85\xb0"), ":a:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x8e"), ":ab:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x85\xb1"), ":b:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x91"), ":cl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x92"), ":cool:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x93"), ":free:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x84\xb9"), ":information_source:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x94"), ":id:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x93\x82"), ":m:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x95"), ":new:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x96"), ":ng:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x85\xbe"), ":o2:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x97"), ":ok:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x85\xbf"), ":parking:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x98"), ":sos:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x99"), ":up:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x86\x9a"), ":vs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\x81"), ":koko:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\x82"), ":sa:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb7"), ":u6708:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb6"), ":u6709:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xaf"), ":u6307:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x89\x90"), ":ideograph_advantage:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb9"), ":u5272:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\x9a"), ":u7121:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb2"), ":u7981:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x89\x91"), ":accept:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb8"), ":u7533:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb4"), ":u5408:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb3"), ":u7a7a:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe3\x8a\x97"), ":congratulations:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe3\x8a\x99"), ":secret:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xba"), ":u55b6:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x88\xb5"), ":u6e80:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x96\xaa"), ":black_small_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x96\xab"), ":white_small_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x97\xbb"), ":white_medium_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x97\xbc"), ":black_medium_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x97\xbd"), ":white_medium_small_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x97\xbe"), ":black_medium_small_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xac\x9b"), ":black_large_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\xac\x9c"), ":white_large_square:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb6"), ":large_orange_diamond:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb7"), ":large_blue_diamond:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb8"), ":small_orange_diamond:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb9"), ":small_blue_diamond:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xba"), ":small_red_triangle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xbb"), ":small_red_triangle_down:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x92\xa0"), ":diamond_shape_with_a_dot_inside:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\x98"), ":radio_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb2"), ":black_square_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb3"), ":white_square_button:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xaa"), ":white_circle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xe2\x9a\xab"), ":black_circle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb4"), ":red_circle:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x94\xb5"), ":blue_circle:" }), +}; + +const QVariantList EmojiModel::flags = { + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8"), ":flag_ac:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9"), ":flag_ad:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa"), ":flag_ae:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xab"), ":flag_af:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xac"), ":flag_ag:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xae"), ":flag_ai:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1"), ":flag_al:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2"), ":flag_am:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4"), ":flag_ao:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6"), ":flag_aq:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7"), ":flag_ar:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8"), ":flag_as:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9"), ":flag_at:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xba"), ":flag_au:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc"), ":flag_aw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd"), ":flag_ax:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf"), ":flag_az:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6"), ":flag_ba:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7"), ":flag_bb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9"), ":flag_bd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa"), ":flag_be:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xab"), ":flag_bf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xac"), ":flag_bg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xad"), ":flag_bh:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xae"), ":flag_bi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf"), ":flag_bj:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1"), ":flag_bl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2"), ":flag_bm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3"), ":flag_bn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4"), ":flag_bo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6"), ":flag_bq:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7"), ":flag_br:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8"), ":flag_bs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9"), ":flag_bt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb"), ":flag_bv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc"), ":flag_bw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe"), ":flag_by:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf"), ":flag_bz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6"), ":flag_ca:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8"), ":flag_cc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9"), ":flag_cd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xab"), ":flag_cf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xac"), ":flag_cg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xad"), ":flag_ch:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xae"), ":flag_ci:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0"), ":flag_ck:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1"), ":flag_cl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2"), ":flag_cm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3"), ":flag_cn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4"), ":flag_co:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5"), ":flag_cp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7"), ":flag_cr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xba"), ":flag_cu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb"), ":flag_cv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc"), ":flag_cw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd"), ":flag_cx:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe"), ":flag_cy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf"), ":flag_cz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa"), ":flag_de:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xac"), ":flag_dg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf"), ":flag_dj:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0"), ":flag_dk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2"), ":flag_dm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4"), ":flag_do:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf"), ":flag_dz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6"), ":flag_ea:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8"), ":flag_ec:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa"), ":flag_ee:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xac"), ":flag_eg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xad"), ":flag_eh:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7"), ":flag_er:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8"), ":flag_es:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9"), ":flag_et:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xba"), ":flag_eu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xae"), ":flag_fi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xaf"), ":flag_fj:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb0"), ":flag_fk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb2"), ":flag_fm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb4"), ":flag_fo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb7"), ":flag_fr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa6"), ":flag_ga:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa7"), ":flag_gb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa9"), ":flag_gd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xaa"), ":flag_ge:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xab"), ":flag_gf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xac"), ":flag_gg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xad"), ":flag_gh:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xae"), ":flag_gi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb1"), ":flag_gl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb2"), ":flag_gm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb3"), ":flag_gn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb5"), ":flag_gp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb6"), ":flag_gq:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb7"), ":flag_gr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb8"), ":flag_gs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb9"), ":flag_gt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xba"), ":flag_gu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbc"), ":flag_gw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbe"), ":flag_gy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb0"), ":flag_hk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb2"), ":flag_hm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb3"), ":flag_hn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb7"), ":flag_hr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb9"), ":flag_ht:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xba"), ":flag_hu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa8"), ":flag_ic:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa9"), ":flag_id:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xaa"), ":flag_ie:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb1"), ":flag_il:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb2"), ":flag_im:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb3"), ":flag_in:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb4"), ":flag_io:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb6"), ":flag_iq:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb7"), ":flag_ir:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb8"), ":flag_is:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb9"), ":flag_it:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa"), ":flag_je:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2"), ":flag_jm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4"), ":flag_jo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5"), ":flag_jp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa"), ":flag_ke:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xac"), ":flag_kg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xad"), ":flag_kh:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xae"), ":flag_ki:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2"), ":flag_km:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3"), ":flag_kn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5"), ":flag_kp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7"), ":flag_kr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc"), ":flag_kw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe"), ":flag_ky:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf"), ":flag_kz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6"), ":flag_la:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7"), ":flag_lb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8"), ":flag_lc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xae"), ":flag_li:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0"), ":flag_lk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7"), ":flag_lr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8"), ":flag_ls:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9"), ":flag_lt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xba"), ":flag_lu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb"), ":flag_lv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe"), ":flag_ly:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6"), ":flag_ma:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8"), ":flag_mc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9"), ":flag_md:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa"), ":flag_me:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xab"), ":flag_mf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xac"), ":flag_mg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xad"), ":flag_mh:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0"), ":flag_mk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1"), ":flag_ml:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2"), ":flag_mm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3"), ":flag_mn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4"), ":flag_mo:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5"), ":flag_mp:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6"), ":flag_mq:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7"), ":flag_mr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8"), ":flag_ms:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9"), ":flag_mt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xba"), ":flag_mu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb"), ":flag_mv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc"), ":flag_mw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd"), ":flag_mx:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe"), ":flag_my:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf"), ":flag_mz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6"), ":flag_na:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8"), ":flag_nc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa"), ":flag_ne:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xab"), ":flag_nf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xac"), ":flag_ng:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xae"), ":flag_ni:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1"), ":flag_nl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4"), ":flag_no:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5"), ":flag_np:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7"), ":flag_nr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xba"), ":flag_nu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf"), ":flag_nz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2"), ":flag_om:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6"), ":flag_pa:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa"), ":flag_pe:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xab"), ":flag_pf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xac"), ":flag_pg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xad"), ":flag_ph:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0"), ":flag_pk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1"), ":flag_pl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2"), ":flag_pm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3"), ":flag_pn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7"), ":flag_pr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8"), ":flag_ps:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9"), ":flag_pt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc"), ":flag_pw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe"), ":flag_py:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6"), ":flag_qa:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa"), ":flag_re:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4"), ":flag_ro:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8"), ":flag_rs:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xba"), ":flag_ru:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc"), ":flag_rw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6"), ":flag_sa:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7"), ":flag_sb:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8"), ":flag_sc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9"), ":flag_sd:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa"), ":flag_se:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xac"), ":flag_sg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xad"), ":flag_sh:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xae"), ":flag_si:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf"), ":flag_sj:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0"), ":flag_sk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1"), ":flag_sl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2"), ":flag_sm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3"), ":flag_sn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4"), ":flag_so:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7"), ":flag_sr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8"), ":flag_ss:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9"), ":flag_st:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb"), ":flag_sv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd"), ":flag_sx:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe"), ":flag_sy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf"), ":flag_sz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6"), ":flag_ta:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8"), ":flag_tc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9"), ":flag_td:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xab"), ":flag_tf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xac"), ":flag_tg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xad"), ":flag_th:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf"), ":flag_tj:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0"), ":flag_tk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1"), ":flag_tl:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2"), ":flag_tm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3"), ":flag_tn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4"), ":flag_to:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7"), ":flag_tr:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9"), ":flag_tt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb"), ":flag_tv:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc"), ":flag_tw:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf"), ":flag_tz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xa6"), ":flag_ua:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xac"), ":flag_ug:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb2"), ":flag_um:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb8"), ":flag_us:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbe"), ":flag_uy:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbf"), ":flag_uz:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6"), ":flag_va:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8"), ":flag_vc:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa"), ":flag_ve:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xac"), ":flag_vg:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xae"), ":flag_vi:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3"), ":flag_vn:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xba"), ":flag_vu:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xab"), ":flag_wf:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8"), ":flag_ws:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0"), ":flag_xk:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa"), ":flag_ye:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9"), ":flag_yt:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6"), ":flag_za:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2"), ":flag_zm:" }), + QVariant::fromValue(Emoji { QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"), ":flag_zw:" }), +}; +} diff --git a/src/gui/emojimodel.h b/src/gui/emojimodel.h new file mode 100644 index 000000000..2d35305d4 --- /dev/null +++ b/src/gui/emojimodel.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#pragma once + +#include <QObject> +#include <QSettings> +#include <QObject> +#include <QQmlEngine> +#include <QVariant> +#include <QVector> +#include <QAbstractItemModel> + +namespace OCC { + +struct Emoji +{ + Emoji(QString u, QString s, bool isCustom = false) + : unicode(std::move(std::move(u))) + , shortname(std::move(std::move(s))) + , isCustom(isCustom) + { + } + Emoji() = default; + + friend QDataStream &operator<<(QDataStream &arch, const Emoji &object) + { + arch << object.unicode; + arch << object.shortname; + return arch; + } + + friend QDataStream &operator>>(QDataStream &arch, Emoji &object) + { + arch >> object.unicode; + arch >> object.shortname; + object.isCustom = object.unicode.startsWith("image://"); + return arch; + } + + QString unicode; + QString shortname; + bool isCustom = false; + + Q_GADGET + Q_PROPERTY(QString unicode MEMBER unicode) + Q_PROPERTY(QString shortname MEMBER shortname) + Q_PROPERTY(bool isCustom MEMBER isCustom) +}; + +class EmojiCategoriesModel : public QAbstractListModel +{ +public: + QVariant data(const QModelIndex &index, int role) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QHash<int, QByteArray> roleNames() const override; + +private: + enum Roles { + EmojiRole = 0, + LabelRole + }; + + struct Category + { + QString emoji; + QString label; + }; + + static const std::vector<Category> categories; +}; + +class EmojiModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariantList model READ model NOTIFY modelChanged) + Q_PROPERTY(QAbstractListModel *emojiCategoriesModel READ emojiCategoriesModel CONSTANT) + + Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged) + + Q_PROPERTY(QVariantList people MEMBER people CONSTANT) + Q_PROPERTY(QVariantList nature MEMBER nature CONSTANT) + Q_PROPERTY(QVariantList food MEMBER food CONSTANT) + Q_PROPERTY(QVariantList activity MEMBER activity CONSTANT) + Q_PROPERTY(QVariantList travel MEMBER travel CONSTANT) + Q_PROPERTY(QVariantList objects MEMBER objects CONSTANT) + Q_PROPERTY(QVariantList symbols MEMBER symbols CONSTANT) + Q_PROPERTY(QVariantList flags MEMBER flags CONSTANT) + +public: + explicit EmojiModel(QObject *parent = nullptr) + : QObject(parent) + { + } + + Q_INVOKABLE QVariantList history() const; + Q_INVOKABLE void setCategory(const QString &category); + Q_INVOKABLE void emojiUsed(const QVariant &modelData); + + QVariantList model() const; + QAbstractListModel *emojiCategoriesModel(); + +signals: + void historyChanged(); + void modelChanged(); + +private: + static const QVariantList people; + static const QVariantList nature; + static const QVariantList food; + static const QVariantList activity; + static const QVariantList travel; + static const QVariantList objects; + static const QVariantList symbols; + static const QVariantList flags; + + QSettings _settings; + QString _category = "history"; + + EmojiCategoriesModel _emojiCategoriesModel; +}; + +} + +Q_DECLARE_METATYPE(OCC::Emoji) diff --git a/src/gui/main.cpp b/src/gui/main.cpp index a7a47e16e..ec4a347eb 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -16,6 +16,7 @@ #include <cmath> #include <csignal> +#include <qqml.h> #ifdef Q_OS_UNIX #include <sys/time.h> @@ -26,6 +27,8 @@ #include "theme.h" #include "common/utility.h" #include "cocoainitializer.h" +#include "userstatusselectormodel.h" +#include "emojimodel.h" #if defined(BUILD_UPDATER) #include "updater/updater.h" @@ -54,6 +57,15 @@ int main(int argc, char **argv) Q_INIT_RESOURCE(resources); Q_INIT_RESOURCE(theme); + qmlRegisterType<EmojiModel>("com.nextcloud.desktopclient", 1, 0, "EmojiModel"); + qRegisterMetaTypeStreamOperators<Emoji>(); + qmlRegisterType<UserStatusSelectorModel>("com.nextcloud.desktopclient", 1, 0, + "UserStatusSelectorModel"); + qmlRegisterUncreatableType<OCC::UserStatus>("com.nextcloud.desktopclient", 1, 0, "UserStatus", + "Access to Status enum"); + qRegisterMetaType<OCC::UserStatus>("UserStatus"); + + // Work around a bug in KDE's qqc2-desktop-style which breaks // buttons with icons not based on a name, by forcing a style name // the platformtheme plugin won't try to force qqc2-desktops-style diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index 0669ebc64..0b3d37582 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -617,7 +617,7 @@ void SocketApi::command_EDIT(const QString &localFile, SocketListener *listener) params.addQueryItem("path", fileData.serverRelativePath); params.addQueryItem("editorId", editor->id()); job->addQueryParams(params); - job->usePOST(); + job->setVerb(JsonApiJob::Verb::Post); QObject::connect(job, &JsonApiJob::jsonReceived, [](const QJsonDocument &json){ auto data = json.object().value("ocs").toObject().value("data").toObject(); diff --git a/src/gui/tray/UserLine.qml b/src/gui/tray/UserLine.qml index 4143acaee..c418e3c1f 100644 --- a/src/gui/tray/UserLine.qml +++ b/src/gui/tray/UserLine.qml @@ -14,6 +14,11 @@ MenuItem { Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Account entry")
+ property variant dialog;
+ property variant comp;
+
+ signal showUserStatusSelectorDialog(int id)
+
RowLayout {
id: userLineLayout
spacing: 0
@@ -189,6 +194,29 @@ MenuItem { }
MenuItem {
+ visible: model.isConnected && model.serverHasUserStatus
+ height: visible ? implicitHeight : 0
+
+ text: qsTr("Set status")
+ font.pixelSize: Style.topLinePixelSize
+ hoverEnabled: true
+ onClicked: {
+ showUserStatusSelectorDialog(index)
+ accountMenu.close()
+ }
+
+ background: Item {
+ height: parent.height
+ width: parent.menu.width
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 1
+ color: parent.parent.hovered ? Style.lightHover : "transparent"
+ }
+ }
+ }
+
+ MenuItem {
text: model.isConnected ? qsTr("Log out") : qsTr("Log in")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
diff --git a/src/gui/tray/UserModel.cpp b/src/gui/tray/UserModel.cpp index 907392571..56a387aa5 100644 --- a/src/gui/tray/UserModel.cpp +++ b/src/gui/tray/UserModel.cpp @@ -4,6 +4,7 @@ #include "accountmanager.h" #include "owncloudgui.h" #include <pushnotifications.h> +#include "userstatusselectormodel.h" #include "syncengine.h" #include "ocsjob.h" #include "configfile.h" @@ -11,7 +12,9 @@ #include "logger.h" #include "guiutility.h" #include "syncfileitem.h" +#include "tray/ActivityListModel.h" #include "tray/NotificationCache.h" +#include "userstatusconnector.h" #include <QDesktopServices> #include <QIcon> @@ -25,8 +28,8 @@ #define NOTIFICATION_REQUEST_FREE_PERIOD 15000 namespace { - constexpr qint64 expiredActivitiesCheckIntervalMsecs = 1000 * 60; - constexpr qint64 activityDefaultExpirationTimeMsecs = 1000 * 60 * 10; +constexpr qint64 expiredActivitiesCheckIntervalMsecs = 1000 * 60; +constexpr qint64 activityDefaultExpirationTimeMsecs = 1000 * 60 * 10; } namespace OCC { @@ -65,7 +68,7 @@ User::User(AccountStatePtr &account, const bool &isCurrent, QObject *parent) connect(this, &User::guiLog, Logger::instance(), &Logger::guiLog); connect(_account->account().data(), &Account::accountChangedAvatar, this, &User::avatarChanged); - connect(_account.data(), &AccountState::statusChanged, this, &User::statusChanged); + connect(_account->account().data(), &Account::userStatusChanged, this, &User::statusChanged); connect(_account.data(), &AccountState::desktopNotificationsAllowedChanged, this, &User::desktopNotificationsAllowedChanged); connect(_activityModel, &ActivityListModel::sendNotificationRequest, this, &User::slotSendNotificationRequest); @@ -236,10 +239,10 @@ void User::slotRefreshActivities() _activityModel->slotRefreshActivity(); } -void User::slotRefreshUserStatus() +void User::slotRefreshUserStatus() { if (_account.data() && _account.data()->isConnected()) { - _account.data()->fetchUserStatus(); + _account->account()->userStatusConnector()->fetchUserStatus(); } } @@ -621,29 +624,29 @@ QString User::server(bool shortened) const return serverUrl; } -UserStatus::Status User::status() const +UserStatus::OnlineStatus User::status() const { - return _account->status(); + return _account->account()->userStatusConnector()->userStatus().state(); } QString User::statusMessage() const { - return _account->statusMessage(); + return _account->account()->userStatusConnector()->userStatus().message(); } QUrl User::statusIcon() const { - return _account->statusIcon(); + return _account->account()->userStatusConnector()->userStatus().stateIcon(); } QString User::statusEmoji() const { - return _account->statusEmoji(); + return _account->account()->userStatusConnector()->userStatus().icon(); } bool User::serverHasUserStatus() const { - return _account->account()->capabilities().userStatus(); + return _account->account()->capabilities().userStatusNotification(); } QImage User::avatar() const @@ -921,6 +924,15 @@ Q_INVOKABLE void UserModel::removeAccount(const int &id) endRemoveRows(); } +std::shared_ptr<OCC::UserStatusConnector> UserModel::userStatusConnector(int id) +{ + if (id < 0 || id >= _users.size()) { + return nullptr; + } + + return _users[id]->account()->userStatusConnector(); +} + int UserModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); diff --git a/src/gui/tray/UserModel.h b/src/gui/tray/UserModel.h index b45fde7c3..c11b2c476 100644 --- a/src/gui/tray/UserModel.h +++ b/src/gui/tray/UserModel.h @@ -12,6 +12,8 @@ #include "accountmanager.h" #include "folderman.h" #include "NotificationCache.h" +#include "userstatusselectormodel.h" +#include "userstatusconnector.h" #include <chrono> namespace OCC { @@ -55,7 +57,7 @@ public: void removeAccount() const; QString avatarUrl() const; bool isDesktopNotificationsAllowed() const; - UserStatus::Status status() const; + UserStatus::OnlineStatus status() const; QString statusMessage() const; QUrl statusIcon() const; QString statusEmoji() const; @@ -158,6 +160,8 @@ public: Q_INVOKABLE void logout(const int &id); Q_INVOKABLE void removeAccount(const int &id); + Q_INVOKABLE std::shared_ptr<OCC::UserStatusConnector> userStatusConnector(int id); + ActivityListModel *currentActivityModel(); enum UserRoles { diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index ccb4af74e..31d5143de 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -140,6 +140,10 @@ Window { }
}
+ Loader {
+ id: userStatusSelectorDialogLoader
+ }
+
Menu {
id: accountMenu
@@ -167,7 +171,14 @@ Window { Instantiator {
id: userLineInstantiator
model: UserModel
- delegate: UserLine {}
+ delegate: UserLine {
+ onShowUserStatusSelectorDialog: {
+ userStatusSelectorDialogLoader.source = "qrc:/qml/src/gui/UserStatusSelectorDialog.qml"
+ userStatusSelectorDialogLoader.item.title = qsTr("Set user status")
+ userStatusSelectorDialogLoader.item.model.load(index)
+ userStatusSelectorDialogLoader.item.show()
+ }
+ }
onObjectAdded: accountMenu.insertItem(index, object)
onObjectRemoved: accountMenu.removeItem(object)
}
diff --git a/src/gui/userstatus.cpp b/src/gui/userstatus.cpp deleted file mode 100644 index 55d929eee..000000000 --- a/src/gui/userstatus.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) by Camila <hello@camila.codes> - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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. - */ - -#include "userstatus.h" -#include "account.h" -#include "accountstate.h" -#include "networkjobs.h" -#include "folderman.h" -#include "creds/abstractcredentials.h" -#include "theme.h" -#include "capabilities.h" - -#include <QTimer> -#include <QJsonDocument> -#include <QJsonObject> - -namespace OCC { - -Q_LOGGING_CATEGORY(lcUserStatus, "nextcloud.gui.userstatus", QtInfoMsg) - -namespace { - UserStatus::Status stringToEnum(const QString &status) - { - // it needs to match the Status enum - const QHash<QString, UserStatus::Status> preDefinedStatus{ - {"online", UserStatus::Status::Online}, - {"dnd", UserStatus::Status::DoNotDisturb}, - {"away", UserStatus::Status::Away}, - {"offline", UserStatus::Status::Offline}, - {"invisible", UserStatus::Status::Invisible} - }; - - // api should return invisible, dnd,... toLower() it is to make sure - // it matches _preDefinedStatus, otherwise the default is online (0) - return preDefinedStatus.value(status.toLower(), UserStatus::Status::Online); - } -} - -UserStatus::UserStatus(QObject *parent) - : QObject(parent) -{ -} - -void UserStatus::fetchUserStatus(AccountPtr account) -{ - if (!account->capabilities().userStatus()) { - return; - } - - if (_job) { - _job->deleteLater(); - } - - _job = new JsonApiJob(account, QStringLiteral("/ocs/v2.php/apps/user_status/api/v1/user_status"), this); - connect(_job.data(), &JsonApiJob::jsonReceived, this, &UserStatus::slotFetchUserStatusFinished); - _job->start(); -} - -void UserStatus::slotFetchUserStatusFinished(const QJsonDocument &json, int statusCode) -{ - const QJsonObject defaultValues { - {"icon", ""}, - {"message", ""}, - {"status", "online"}, - {"messageIsPredefined", "false"}, - {"statusIsUserDefined", "false"} - }; - - if (statusCode != 200) { - qCInfo(lcUserStatus) << "Slot fetch UserStatus finished with status code" << statusCode; - qCInfo(lcUserStatus) << "Using then default values as if user has not set any status" << defaultValues; - } - - const auto retrievedData = json.object().value("ocs").toObject().value("data").toObject(defaultValues); - - _emoji = retrievedData.value("icon").toString().trimmed(); - _status = stringToEnum(retrievedData.value("status").toString()); - _message = retrievedData.value("message").toString().trimmed(); - - emit fetchUserStatusFinished(); -} - -UserStatus::Status UserStatus::status() const -{ - return _status; -} - -QString UserStatus::message() const -{ - return _message; -} - -QString UserStatus::emoji() const -{ - return _emoji; -} - -QUrl UserStatus::icon() const -{ - switch (_status) { - case Status::Away: - return Theme::instance()->statusAwayImageSource(); - case Status::DoNotDisturb: - return Theme::instance()->statusDoNotDisturbImageSource(); - case Status::Invisible: - case Status::Offline: - return Theme::instance()->statusInvisibleImageSource(); - case Status::Online: - return Theme::instance()->statusOnlineImageSource(); - } - - Q_UNREACHABLE(); -} - -} // namespace OCC diff --git a/src/gui/userstatus.h b/src/gui/userstatus.h deleted file mode 100644 index 3e952a50b..000000000 --- a/src/gui/userstatus.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) by Camila <hello@camila.codes> - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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. - */ - -#ifndef USERSTATUS_H -#define USERSTATUS_H - -#include <QPointer> -#include "accountfwd.h" - -namespace OCC { - -class JsonApiJob; - -class UserStatus : public QObject -{ - Q_OBJECT - -public: - explicit UserStatus(QObject *parent = nullptr); - enum class Status { - Online, - DoNotDisturb, - Away, - Offline, - Invisible - }; - Q_ENUM(Status); - void fetchUserStatus(AccountPtr account); - Status status() const; - QString message() const; - QString emoji() const; - QUrl icon() const; - -private slots: - void slotFetchUserStatusFinished(const QJsonDocument &json, int statusCode); - -signals: - void fetchUserStatusFinished(); - -private: - QPointer<JsonApiJob> _job; // the currently running job - Status _status = Status::Online; - QString _message; - QString _emoji; -}; - - -} // namespace OCC - -#endif //USERSTATUS_H diff --git a/src/gui/userstatusselectormodel.cpp b/src/gui/userstatusselectormodel.cpp new file mode 100644 index 000000000..c241474e0 --- /dev/null +++ b/src/gui/userstatusselectormodel.cpp @@ -0,0 +1,474 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#include "userstatusselectormodel.h" +#include "tray/UserModel.h" + +#include <ocsuserstatusconnector.h> +#include <qnamespace.h> +#include <userstatusconnector.h> +#include <theme.h> + +#include <QDateTime> +#include <QLoggingCategory> + +#include <algorithm> +#include <cmath> +#include <cstddef> + +namespace OCC { + +Q_LOGGING_CATEGORY(lcUserStatusDialogModel, "nextcloud.gui.userstatusdialogmodel", QtInfoMsg) + +UserStatusSelectorModel::UserStatusSelectorModel(QObject *parent) + : QObject(parent) + , _dateTimeProvider(new DateTimeProvider) +{ + _userStatus.setIcon("😀"); +} + +UserStatusSelectorModel::UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, QObject *parent) + : QObject(parent) + , _userStatusConnector(userStatusConnector) + , _userStatus("no-id", "", "😀", UserStatus::OnlineStatus::Online, false, {}) + , _dateTimeProvider(new DateTimeProvider) +{ + _userStatus.setIcon("😀"); + init(); +} + +UserStatusSelectorModel::UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, + std::unique_ptr<DateTimeProvider> dateTimeProvider, + QObject *parent) + : QObject(parent) + , _userStatusConnector(userStatusConnector) + , _dateTimeProvider(std::move(dateTimeProvider)) +{ + _userStatus.setIcon("😀"); + init(); +} + +UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus, + std::unique_ptr<DateTimeProvider> dateTimeProvider, QObject *parent) + : QObject(parent) + , _userStatus(userStatus) + , _dateTimeProvider(std::move(dateTimeProvider)) +{ + _userStatus.setIcon("😀"); +} + +UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus, + QObject *parent) + : QObject(parent) + , _userStatus(userStatus) +{ + _userStatus.setIcon("😀"); +} + +void UserStatusSelectorModel::load(int id) +{ + reset(); + _userStatusConnector = UserModel::instance()->userStatusConnector(id); + init(); +} + +void UserStatusSelectorModel::reset() +{ + if (_userStatusConnector) { + disconnect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, + &UserStatusSelectorModel::onUserStatusFetched); + disconnect(_userStatusConnector.get(), &UserStatusConnector::predefinedStatusesFetched, this, + &UserStatusSelectorModel::onPredefinedStatusesFetched); + disconnect(_userStatusConnector.get(), &UserStatusConnector::error, this, + &UserStatusSelectorModel::onError); + disconnect(_userStatusConnector.get(), &UserStatusConnector::userStatusSet, this, + &UserStatusSelectorModel::onUserStatusSet); + disconnect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this, + &UserStatusSelectorModel::onMessageCleared); + } + _userStatusConnector = nullptr; +} + +void UserStatusSelectorModel::init() +{ + if (!_userStatusConnector) { + return; + } + + connect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, + &UserStatusSelectorModel::onUserStatusFetched); + connect(_userStatusConnector.get(), &UserStatusConnector::predefinedStatusesFetched, this, + &UserStatusSelectorModel::onPredefinedStatusesFetched); + connect(_userStatusConnector.get(), &UserStatusConnector::error, this, + &UserStatusSelectorModel::onError); + connect(_userStatusConnector.get(), &UserStatusConnector::userStatusSet, this, + &UserStatusSelectorModel::onUserStatusSet); + connect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this, + &UserStatusSelectorModel::onMessageCleared); + + _userStatusConnector->fetchUserStatus(); + _userStatusConnector->fetchPredefinedStatuses(); +} + +UserStatusSelectorModel::~UserStatusSelectorModel() +{ + qCDebug(lcUserStatusDialogModel) << "Destroyed"; +} + +void UserStatusSelectorModel::onUserStatusSet() +{ + qCDebug(lcUserStatusDialogModel) << "Emit finished"; + emit finished(); +} + +void UserStatusSelectorModel::onMessageCleared() +{ + emit finished(); +} + +void UserStatusSelectorModel::onError(UserStatusConnector::Error error) +{ + qCWarning(lcUserStatusDialogModel) << "Error:" << error; + + switch (error) { + case UserStatusConnector::Error::CouldNotFetchPredefinedUserStatuses: + setError(tr("Could not fetch predefined statuses. Make sure you are connected to the server.")); + return; + + case UserStatusConnector::Error::CouldNotFetchUserStatus: + setError(tr("Could not fetch user status. Make sure you are connected to the server.")); + return; + + case UserStatusConnector::Error::UserStatusNotSupported: + setError(tr("User status feature is not supported. You will not be able to set your user status.")); + return; + + case UserStatusConnector::Error::EmojisNotSupported: + setError(tr("Emojis feature is not supported. Some user status functionality may not work.")); + return; + + case UserStatusConnector::Error::CouldNotSetUserStatus: + setError(tr("Could not set user status. Make sure you are connected to the server.")); + return; + + case UserStatusConnector::Error::CouldNotClearMessage: + setError(tr("Could not clear user status message. Make sure you are connected to the server.")); + return; + } + + Q_UNREACHABLE(); +} + +void UserStatusSelectorModel::setError(const QString &reason) +{ + _errorMessage = reason; + emit errorMessageChanged(); +} + +void UserStatusSelectorModel::clearError() +{ + setError(""); +} + +void UserStatusSelectorModel::setOnlineStatus(UserStatus::OnlineStatus status) +{ + if (status == _userStatus.state()) { + return; + } + + _userStatus.setState(status); + emit onlineStatusChanged(); +} + +QUrl UserStatusSelectorModel::onlineIcon() const +{ + return Theme::instance()->statusOnlineImageSource(); +} + +QUrl UserStatusSelectorModel::awayIcon() const +{ + return Theme::instance()->statusAwayImageSource(); +} +QUrl UserStatusSelectorModel::dndIcon() const +{ + return Theme::instance()->statusDoNotDisturbImageSource(); +} +QUrl UserStatusSelectorModel::invisibleIcon() const +{ + return Theme::instance()->statusInvisibleImageSource(); +} + +UserStatus::OnlineStatus UserStatusSelectorModel::onlineStatus() const +{ + return _userStatus.state(); +} + +QString UserStatusSelectorModel::userStatusMessage() const +{ + return _userStatus.message(); +} + +void UserStatusSelectorModel::setUserStatusMessage(const QString &message) +{ + _userStatus.setMessage(message); + _userStatus.setMessagePredefined(false); + emit userStatusChanged(); +} + +void UserStatusSelectorModel::setUserStatusEmoji(const QString &emoji) +{ + _userStatus.setIcon(emoji); + _userStatus.setMessagePredefined(false); + emit userStatusChanged(); +} + +QString UserStatusSelectorModel::userStatusEmoji() const +{ + return _userStatus.icon(); +} + +void UserStatusSelectorModel::onUserStatusFetched(const UserStatus &userStatus) +{ + if (userStatus.state() != UserStatus::OnlineStatus::Offline) { + _userStatus.setState(userStatus.state()); + } + _userStatus.setMessage(userStatus.message()); + _userStatus.setMessagePredefined(userStatus.messagePredefined()); + _userStatus.setId(userStatus.id()); + _userStatus.setClearAt(userStatus.clearAt()); + + if (!userStatus.icon().isEmpty()) { + _userStatus.setIcon(userStatus.icon()); + } + + emit userStatusChanged(); + emit onlineStatusChanged(); + emit clearAtChanged(); +} + +Optional<ClearAt> UserStatusSelectorModel::clearStageTypeToDateTime(ClearStageType type) const +{ + switch (type) { + case ClearStageType::DontClear: + return {}; + + case ClearStageType::HalfHour: { + ClearAt clearAt; + clearAt._type = ClearAtType::Period; + clearAt._period = 60 * 30; + return clearAt; + } + + case ClearStageType::OneHour: { + ClearAt clearAt; + clearAt._type = ClearAtType::Period; + clearAt._period = 60 * 60; + return clearAt; + } + + case ClearStageType::FourHour: { + ClearAt clearAt; + clearAt._type = ClearAtType::Period; + clearAt._period = 60 * 60 * 4; + return clearAt; + } + + case ClearStageType::Today: { + ClearAt clearAt; + clearAt._type = ClearAtType::EndOf; + clearAt._endof = "day"; + return clearAt; + } + + case ClearStageType::Week: { + ClearAt clearAt; + clearAt._type = ClearAtType::EndOf; + clearAt._endof = "week"; + return clearAt; + } + + default: + Q_UNREACHABLE(); + } +} + +void UserStatusSelectorModel::setUserStatus() +{ + Q_ASSERT(_userStatusConnector); + if (!_userStatusConnector) { + return; + } + + clearError(); + _userStatusConnector->setUserStatus(_userStatus); +} + +void UserStatusSelectorModel::clearUserStatus() +{ + Q_ASSERT(_userStatusConnector); + if (!_userStatusConnector) { + return; + } + + clearError(); + _userStatusConnector->clearMessage(); +} + +void UserStatusSelectorModel::onPredefinedStatusesFetched(const std::vector<UserStatus> &statuses) +{ + _predefinedStatuses = statuses; + emit predefinedStatusesChanged(); +} + +UserStatus UserStatusSelectorModel::predefinedStatus(int index) const +{ + Q_ASSERT(0 <= index && index < static_cast<int>(_predefinedStatuses.size())); + return _predefinedStatuses[index]; +} + +int UserStatusSelectorModel::predefinedStatusesCount() const +{ + return static_cast<int>(_predefinedStatuses.size()); +} + +void UserStatusSelectorModel::setPredefinedStatus(int index) +{ + Q_ASSERT(0 <= index && index < static_cast<int>(_predefinedStatuses.size())); + + _userStatus.setMessagePredefined(true); + const auto predefinedStatus = _predefinedStatuses[index]; + _userStatus.setId(predefinedStatus.id()); + _userStatus.setMessage(predefinedStatus.message()); + _userStatus.setIcon(predefinedStatus.icon()); + _userStatus.setClearAt(predefinedStatus.clearAt()); + + emit userStatusChanged(); + emit clearAtChanged(); +} + +QString UserStatusSelectorModel::clearAtStageToString(ClearStageType stage) const +{ + switch (stage) { + case ClearStageType::DontClear: + return tr("Don't clear"); + + case ClearStageType::HalfHour: + return tr("30 minutes"); + + case ClearStageType::OneHour: + return tr("1 hour"); + + case ClearStageType::FourHour: + return tr("4 hours"); + + case ClearStageType::Today: + return tr("Today"); + + case ClearStageType::Week: + return tr("This week"); + + default: + Q_UNREACHABLE(); + } +} + +QStringList UserStatusSelectorModel::clearAtValues() const +{ + QStringList clearAtStages; + std::transform(_clearStages.begin(), _clearStages.end(), + std::back_inserter(clearAtStages), + [this](const ClearStageType &stage) { return clearAtStageToString(stage); }); + + return clearAtStages; +} + +void UserStatusSelectorModel::setClearAt(int index) +{ + Q_ASSERT(0 <= index && index < static_cast<int>(_clearStages.size())); + _userStatus.setClearAt(clearStageTypeToDateTime(_clearStages[index])); + emit clearAtChanged(); +} + +QString UserStatusSelectorModel::errorMessage() const +{ + return _errorMessage; +} + +QString UserStatusSelectorModel::timeDifferenceToString(int differenceSecs) const +{ + if (differenceSecs < 60) { + return tr("Less than a minute"); + } else if (differenceSecs < 60 * 60) { + const auto minutesLeft = std::ceil(differenceSecs / 60.0); + if (minutesLeft == 1) { + return tr("1 minute"); + } else { + return tr("%1 minutes").arg(minutesLeft); + } + } else if (differenceSecs < 60 * 60 * 24) { + const auto hoursLeft = std::ceil(differenceSecs / 60.0 / 60.0); + if (hoursLeft == 1) { + return tr("1 hour"); + } else { + return tr("%1 hours").arg(hoursLeft); + } + } else { + const auto daysLeft = std::ceil(differenceSecs / 60.0 / 60.0 / 24.0); + if (daysLeft == 1) { + return tr("1 day"); + } else { + return tr("%1 days").arg(daysLeft); + } + } +} + +QString UserStatusSelectorModel::clearAtReadable(const Optional<ClearAt> &clearAt) const +{ + if (clearAt) { + switch (clearAt->_type) { + case ClearAtType::Period: { + return timeDifferenceToString(clearAt->_period); + } + + case ClearAtType::Timestamp: { + const int difference = static_cast<int>(clearAt->_timestamp - _dateTimeProvider->currentDateTime().toTime_t()); + return timeDifferenceToString(difference); + } + + case ClearAtType::EndOf: { + if (clearAt->_endof == "day") { + return tr("Today"); + } else if (clearAt->_endof == "week") { + return tr("This week"); + } + Q_UNREACHABLE(); + } + + default: + Q_UNREACHABLE(); + } + } + return tr("Don't clear"); +} + +QString UserStatusSelectorModel::predefinedStatusClearAt(int index) const +{ + return clearAtReadable(predefinedStatus(index).clearAt()); +} + +QString UserStatusSelectorModel::clearAt() const +{ + return clearAtReadable(_userStatus.clearAt()); +} +} diff --git a/src/gui/userstatusselectormodel.h b/src/gui/userstatusselectormodel.h new file mode 100644 index 000000000..cc90b777d --- /dev/null +++ b/src/gui/userstatusselectormodel.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#pragma once + +#include "common/result.h" + +#include <userstatusconnector.h> +#include <datetimeprovider.h> + +#include <QObject> +#include <QMetaType> +#include <QtNumeric> + +#include <cstddef> +#include <memory> +#include <vector> + +namespace OCC { + +class UserStatusSelectorModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString userStatusMessage READ userStatusMessage NOTIFY userStatusChanged) + Q_PROPERTY(QString userStatusEmoji READ userStatusEmoji WRITE setUserStatusEmoji NOTIFY userStatusChanged) + Q_PROPERTY(OCC::UserStatus::OnlineStatus onlineStatus READ onlineStatus WRITE setOnlineStatus NOTIFY onlineStatusChanged) + Q_PROPERTY(int predefinedStatusesCount READ predefinedStatusesCount NOTIFY predefinedStatusesChanged) + Q_PROPERTY(QStringList clearAtValues READ clearAtValues CONSTANT) + Q_PROPERTY(QString clearAt READ clearAt NOTIFY clearAtChanged) + Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) + Q_PROPERTY(QUrl onlineIcon READ onlineIcon CONSTANT) + Q_PROPERTY(QUrl awayIcon READ awayIcon CONSTANT) + Q_PROPERTY(QUrl dndIcon READ dndIcon CONSTANT) + Q_PROPERTY(QUrl invisibleIcon READ invisibleIcon CONSTANT) + +public: + explicit UserStatusSelectorModel(QObject *parent = nullptr); + + explicit UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, + QObject *parent = nullptr); + + explicit UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, + std::unique_ptr<DateTimeProvider> dateTimeProvider, + QObject *parent = nullptr); + + explicit UserStatusSelectorModel(const UserStatus &userStatus, + std::unique_ptr<DateTimeProvider> dateTimeProvider, + QObject *parent = nullptr); + + explicit UserStatusSelectorModel(const UserStatus &userStatus, + QObject *parent = nullptr); + + ~UserStatusSelectorModel() override; + + Q_INVOKABLE void load(int id); + + Q_REQUIRED_RESULT UserStatus::OnlineStatus onlineStatus() const; + Q_INVOKABLE void setOnlineStatus(OCC::UserStatus::OnlineStatus status); + + Q_REQUIRED_RESULT QUrl onlineIcon() const; + Q_REQUIRED_RESULT QUrl awayIcon() const; + Q_REQUIRED_RESULT QUrl dndIcon() const; + Q_REQUIRED_RESULT QUrl invisibleIcon() const; + + Q_REQUIRED_RESULT QString userStatusMessage() const; + Q_INVOKABLE void setUserStatusMessage(const QString &message); + void setUserStatusEmoji(const QString &emoji); + Q_REQUIRED_RESULT QString userStatusEmoji() const; + + Q_INVOKABLE void setUserStatus(); + Q_INVOKABLE void clearUserStatus(); + + Q_REQUIRED_RESULT int predefinedStatusesCount() const; + Q_INVOKABLE UserStatus predefinedStatus(int index) const; + Q_INVOKABLE QString predefinedStatusClearAt(int index) const; + Q_INVOKABLE void setPredefinedStatus(int index); + + Q_REQUIRED_RESULT QStringList clearAtValues() const; + Q_REQUIRED_RESULT QString clearAt() const; + Q_INVOKABLE void setClearAt(int index); + + Q_REQUIRED_RESULT QString errorMessage() const; + +signals: + void errorMessageChanged(); + void userStatusChanged(); + void onlineStatusChanged(); + void clearAtChanged(); + void predefinedStatusesChanged(); + void finished(); + +private: + enum class ClearStageType { + DontClear, + HalfHour, + OneHour, + FourHour, + Today, + Week + }; + + void init(); + void reset(); + void onUserStatusFetched(const UserStatus &userStatus); + void onPredefinedStatusesFetched(const std::vector<UserStatus> &statuses); + void onUserStatusSet(); + void onMessageCleared(); + void onError(UserStatusConnector::Error error); + + Q_REQUIRED_RESULT QString clearAtStageToString(ClearStageType stage) const; + Q_REQUIRED_RESULT QString clearAtReadable(const Optional<ClearAt> &clearAt) const; + Q_REQUIRED_RESULT QString timeDifferenceToString(int differenceSecs) const; + Q_REQUIRED_RESULT Optional<ClearAt> clearStageTypeToDateTime(ClearStageType type) const; + void setError(const QString &reason); + void clearError(); + + std::shared_ptr<UserStatusConnector> _userStatusConnector {}; + std::vector<UserStatus> _predefinedStatuses; + UserStatus _userStatus; + std::unique_ptr<DateTimeProvider> _dateTimeProvider; + + QString _errorMessage; + + std::vector<ClearStageType> _clearStages = { + ClearStageType::DontClear, + ClearStageType::HalfHour, + ClearStageType::OneHour, + ClearStageType::FourHour, + ClearStageType::Today, + ClearStageType::Week + }; +}; +} diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index 3c38c6374..750d45ec8 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -55,6 +55,9 @@ set(libsync_SRCS theme.cpp clientsideencryption.cpp clientsideencryptionjobs.cpp + datetimeprovider.cpp + ocsuserstatusconnector.cpp + userstatusconnector.cpp creds/dummycredentials.cpp creds/abstractcredentials.cpp creds/credentialscommon.cpp diff --git a/src/libsync/abstractnetworkjob.cpp b/src/libsync/abstractnetworkjob.cpp index be9c01c93..6bb00a6b2 100644 --- a/src/libsync/abstractnetworkjob.cpp +++ b/src/libsync/abstractnetworkjob.cpp @@ -139,6 +139,15 @@ QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUr return reply; } +QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUrl &url, + QNetworkRequest req, const QByteArray &requestBody) +{ + auto reply = _account->sendRawRequest(verb, url, req, requestBody); + _requestBody = nullptr; + adoptRequest(reply); + return reply; +} + void AbstractNetworkJob::adoptRequest(QNetworkReply *reply) { addTimer(reply); diff --git a/src/libsync/abstractnetworkjob.h b/src/libsync/abstractnetworkjob.h index d1f3f6bc5..63391e672 100644 --- a/src/libsync/abstractnetworkjob.h +++ b/src/libsync/abstractnetworkjob.h @@ -128,6 +128,9 @@ protected: QNetworkRequest req = QNetworkRequest(), QIODevice *requestBody = nullptr); + QNetworkReply *sendRequest(const QByteArray &verb, const QUrl &url, + QNetworkRequest req, const QByteArray &requestBody); + // sendRequest does not take a relative path instead of an url, // but the old API allowed that. We have this undefined overload // to help catch usage errors diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 8beb34412..d0cfe36e4 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -13,6 +13,8 @@ */ #include "account.h" +#include "accountfwd.h" +#include "clientsideencryptionjobs.h" #include "cookiejar.h" #include "networkjobs.h" #include "configfile.h" @@ -27,6 +29,7 @@ #include "common/asserts.h" #include "clientsideencryption.h" +#include "ocsuserstatusconnector.h" #include <QLoggingCategory> #include <QNetworkReply> @@ -43,6 +46,7 @@ #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> +#include <QLoggingCategory> #include <qsslconfiguration.h> #include <qt5keychain/keychain.h> @@ -93,6 +97,7 @@ QString Account::davPath() const void Account::setSharedThis(AccountPtr sharedThis) { _sharedThis = sharedThis.toWeakRef(); + setupUserStatusConnector(); } QString Account::davPathBase() @@ -337,6 +342,24 @@ QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url, return _am->sendCustomRequest(req, verb, data); } +QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, const QByteArray &data) +{ + req.setUrl(url); + req.setSslConfiguration(this->getOrCreateSslConfig()); + if (verb == "HEAD" && data.isEmpty()) { + return _am->head(req); + } else if (verb == "GET" && data.isEmpty()) { + return _am->get(req); + } else if (verb == "POST") { + return _am->post(req, data); + } else if (verb == "PUT") { + return _am->put(req, data); + } else if (verb == "DELETE" && data.isEmpty()) { + return _am->deleteResource(req); + } + return _am->sendCustomRequest(req, verb, data); +} + SimpleNetworkJob *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data) { auto job = new SimpleNetworkJob(sharedFromThis()); @@ -544,9 +567,18 @@ void Account::setCapabilities(const QVariantMap &caps) { _capabilities = Capabilities(caps); + setupUserStatusConnector(); trySetupPushNotifications(); } +void Account::setupUserStatusConnector() +{ + _userStatusConnector = std::make_shared<OcsUserStatusConnector>(sharedFromThis()); + connect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, [this](const UserStatus &) { + emit userStatusChanged(); + }); +} + QString Account::serverVersion() const { return _serverVersion; @@ -744,4 +776,9 @@ PushNotifications *Account::pushNotifications() const return _pushNotifications; } +std::shared_ptr<UserStatusConnector> Account::userStatusConnector() const +{ + return _userStatusConnector; +} + } // namespace OCC diff --git a/src/libsync/account.h b/src/libsync/account.h index 00930b2b3..334425853 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -55,6 +55,7 @@ using AccountPtr = QSharedPointer<Account>; class AccessManager; class SimpleNetworkJob; class PushNotifications; +class UserStatusConnector; /** * @brief Reimplement this to handle SSL errors from libsync @@ -150,6 +151,9 @@ public: QNetworkRequest req = QNetworkRequest(), QIODevice *data = nullptr); + QNetworkReply *sendRawRequest(const QByteArray &verb, + const QUrl &url, QNetworkRequest req, const QByteArray &data); + /** Create and start network job for a simple one-off request. * * More complicated requests typically create their own job types. @@ -251,10 +255,13 @@ public: // Check for the directEditing capability void fetchDirectEditors(const QUrl &directEditingURL, const QString &directEditingETag); + void setupUserStatusConnector(); void trySetupPushNotifications(); PushNotifications *pushNotifications() const; void setPushNotificationsReconnectInterval(int interval); + std::shared_ptr<UserStatusConnector> userStatusConnector() const; + public slots: /// Used when forgetting credentials void clearQNAMCache(); @@ -287,6 +294,8 @@ signals: void pushNotificationsReady(Account *account); void pushNotificationsDisabled(Account *account); + void userStatusChanged(); + protected Q_SLOTS: void slotCredentialsFetched(); void slotCredentialsAsked(); @@ -343,6 +352,8 @@ private: PushNotifications *_pushNotifications = nullptr; + std::shared_ptr<UserStatusConnector> _userStatusConnector; + /* IMPORTANT - remove later - FIXME MS@2019-12-07 --> * TODO: For "Log out" & "Remove account": Remove client CA certs and KEY! * diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp index 04db298b1..9983821ae 100644 --- a/src/libsync/capabilities.cpp +++ b/src/libsync/capabilities.cpp @@ -187,13 +187,31 @@ bool Capabilities::chunkingNg() const return _capabilities["dav"].toMap()["chunking"].toByteArray() >= "1.0"; } -bool Capabilities::userStatus() const +bool Capabilities::userStatusNotification() const { return _capabilities.contains("notifications") && _capabilities["notifications"].toMap().contains("ocs-endpoints") && _capabilities["notifications"].toMap()["ocs-endpoints"].toStringList().contains("user-status"); } +bool Capabilities::userStatus() const +{ + if (!_capabilities.contains("user_status")) { + return false; + } + const auto userStatusMap = _capabilities["user_status"].toMap(); + return userStatusMap.value("enabled", false).toBool(); +} + +bool Capabilities::userStatusSupportsEmoji() const +{ + if (!userStatus()) { + return false; + } + const auto userStatusMap = _capabilities["user_status"].toMap(); + return userStatusMap.value("supports_emoji", false).toBool(); +} + PushNotificationTypes Capabilities::availablePushNotifications() const { if (!_capabilities.contains("notify_push")) { diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h index 078a0cd35..3040db890 100644 --- a/src/libsync/capabilities.h +++ b/src/libsync/capabilities.h @@ -58,7 +58,9 @@ public: bool sharePublicLinkMultiple() const; bool shareResharing() const; bool chunkingNg() const; + bool userStatusNotification() const; bool userStatus() const; + bool userStatusSupportsEmoji() const; /// Returns which kind of push notfications are available PushNotificationTypes availablePushNotifications() const; diff --git a/src/libsync/clientsideencryption.cpp b/src/libsync/clientsideencryption.cpp index 4ea561da3..2a8b1a189 100644 --- a/src/libsync/clientsideencryption.cpp +++ b/src/libsync/clientsideencryption.cpp @@ -56,7 +56,8 @@ Q_LOGGING_CATEGORY(lcCse, "nextcloud.sync.clientsideencryption", QtInfoMsg) Q_LOGGING_CATEGORY(lcCseDecryption, "nextcloud.e2e", QtInfoMsg) Q_LOGGING_CATEGORY(lcCseMetadata, "nextcloud.metadata", QtInfoMsg) -QString baseUrl(){ +QString e2eeBaseUrl() +{ return QStringLiteral("ocs/v2.php/apps/end_to_end_encryption/api/v1/"); } @@ -1180,7 +1181,7 @@ void ClientSideEncryption::generateCSR(const AccountPtr &account, EVP_PKEY *keyP qCInfo(lcCse()) << "Returning the certificate"; qCInfo(lcCse()) << output; - auto job = new SignPublicKeyApiJob(account, baseUrl() + "public-key", this); + auto job = new SignPublicKeyApiJob(account, e2eeBaseUrl() + "public-key", this); job->setCsr(output); connect(job, &SignPublicKeyApiJob::jsonReceived, [this, account](const QJsonDocument& json, int retCode) { @@ -1212,7 +1213,7 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account) auto cryptedText = EncryptionHelper::encryptPrivateKey(secretKey, EncryptionHelper::privateKeyToPem(_privateKey), salt); // Send private key to the server - auto job = new StorePrivateKeyApiJob(account, baseUrl() + "private-key", this); + auto job = new StorePrivateKeyApiJob(account, e2eeBaseUrl() + "private-key", this); job->setPrivateKey(cryptedText); connect(job, &StorePrivateKeyApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) { Q_UNUSED(doc); @@ -1296,7 +1297,7 @@ void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QB void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account) { qCInfo(lcCse()) << "Retrieving private key from server"; - auto job = new JsonApiJob(account, baseUrl() + "private-key", this); + auto job = new JsonApiJob(account, e2eeBaseUrl() + "private-key", this); connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) { if (retCode == 200) { QString key = doc.object()["ocs"].toObject()["data"].toObject()["private-key"].toString(); @@ -1315,7 +1316,7 @@ void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account) void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account) { qCInfo(lcCse()) << "Retrieving public key from server"; - auto job = new JsonApiJob(account, baseUrl() + "public-key", this); + auto job = new JsonApiJob(account, e2eeBaseUrl() + "public-key", this); connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) { if (retCode == 200) { QString publicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-keys"].toObject()[account->davUser()].toString(); @@ -1336,7 +1337,7 @@ void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account) void ClientSideEncryption::fetchAndValidatePublicKeyFromServer(const AccountPtr &account) { qCInfo(lcCse()) << "Retrieving public key from server"; - auto job = new JsonApiJob(account, baseUrl() + "server-key", this); + auto job = new JsonApiJob(account, e2eeBaseUrl() + "server-key", this); connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) { if (retCode == 200) { const auto serverPublicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-key"].toString().toLatin1(); diff --git a/src/libsync/clientsideencryption.h b/src/libsync/clientsideencryption.h index e89586451..751c7d4c8 100644 --- a/src/libsync/clientsideencryption.h +++ b/src/libsync/clientsideencryption.h @@ -23,7 +23,7 @@ class ReadPasswordJob; namespace OCC { -QString baseUrl(); +QString e2eeBaseUrl(); namespace EncryptionHelper { QByteArray generateRandomFilename(); diff --git a/src/libsync/clientsideencryptionjobs.cpp b/src/libsync/clientsideencryptionjobs.cpp index ad8e30c66..71bb8a510 100644 --- a/src/libsync/clientsideencryptionjobs.cpp +++ b/src/libsync/clientsideencryptionjobs.cpp @@ -27,7 +27,7 @@ namespace OCC { GetMetadataApiJob::GetMetadataApiJob(const AccountPtr& account, const QByteArray& fileId, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId) +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId) { } @@ -63,7 +63,7 @@ StoreMetaDataApiJob::StoreMetaDataApiJob(const AccountPtr& account, const QByteArray& fileId, const QByteArray& b64Metadata, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId), _b64Metadata(b64Metadata) +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId), _b64Metadata(b64Metadata) { } @@ -104,8 +104,8 @@ UpdateMetadataApiJob::UpdateMetadataApiJob(const AccountPtr& account, const QByteArray& b64Metadata, const QByteArray& token, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), -_fileId(fileId), +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent) +, _fileId(fileId), _b64Metadata(b64Metadata), _token(token) { @@ -154,7 +154,7 @@ UnlockEncryptFolderApiJob::UnlockEncryptFolderApiJob(const AccountPtr& account, const QByteArray& fileId, const QByteArray& token, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId), _token(token) +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId), _token(token) { } @@ -185,11 +185,10 @@ bool UnlockEncryptFolderApiJob::finished() } - DeleteMetadataApiJob::DeleteMetadataApiJob(const AccountPtr& account, const QByteArray& fileId, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId) +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId) { } @@ -219,7 +218,7 @@ bool DeleteMetadataApiJob::finished() } LockEncryptFolderApiJob::LockEncryptFolderApiJob(const AccountPtr& account, const QByteArray& fileId, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId) +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId) { } @@ -258,7 +257,7 @@ bool LockEncryptFolderApiJob::finished() } SetEncryptionFlagApiJob::SetEncryptionFlagApiJob(const AccountPtr& account, const QByteArray& fileId, FlagAction flagAction, QObject* parent) -: AbstractNetworkJob(account, baseUrl() + QStringLiteral("encrypted/") + fileId, parent), _fileId(fileId), _flagAction(flagAction) +: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("encrypted/") + fileId, parent), _fileId(fileId), _flagAction(flagAction) { } diff --git a/src/libsync/datetimeprovider.cpp b/src/libsync/datetimeprovider.cpp new file mode 100644 index 000000000..084072312 --- /dev/null +++ b/src/libsync/datetimeprovider.cpp @@ -0,0 +1,17 @@ +#include "datetimeprovider.h" + +namespace OCC { + +DateTimeProvider::~DateTimeProvider() = default; + +QDateTime DateTimeProvider::currentDateTime() const +{ + return QDateTime::currentDateTime(); +} + +QDate DateTimeProvider::currentDate() const +{ + return QDate::currentDate(); +} + +} diff --git a/src/libsync/datetimeprovider.h b/src/libsync/datetimeprovider.h new file mode 100644 index 000000000..1525a2e3d --- /dev/null +++ b/src/libsync/datetimeprovider.h @@ -0,0 +1,18 @@ +#pragma once + +#include "owncloudlib.h" + +#include <QDateTime> + +namespace OCC { + +class OWNCLOUDSYNC_EXPORT DateTimeProvider +{ +public: + virtual ~DateTimeProvider(); + + virtual QDateTime currentDateTime() const; + + virtual QDate currentDate() const; +}; +} diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp index 9224afc76..2800af0d6 100644 --- a/src/libsync/networkjobs.cpp +++ b/src/libsync/networkjobs.cpp @@ -830,13 +830,49 @@ void JsonApiJob::addRawHeader(const QByteArray &headerName, const QByteArray &va _request.setRawHeader(headerName, value); } +void JsonApiJob::setBody(const QJsonDocument &body) +{ + _body = body.toJson(); + qCDebug(lcJsonApiJob) << "Set body for request:" << _body; + if (!_body.isEmpty()) { + _request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + } +} + + +void JsonApiJob::setVerb(Verb value) +{ + _verb = value; +} + + +QByteArray JsonApiJob::verbToString() const +{ + switch (_verb) { + case Verb::Get: + return "GET"; + case Verb::Post: + return "POST"; + case Verb::Put: + return "PUT"; + case Verb::Delete: + return "DELETE"; + } + return "GET"; +} + void JsonApiJob::start() { addRawHeader("OCS-APIREQUEST", "true"); auto query = _additionalParams; query.addQueryItem(QLatin1String("format"), QLatin1String("json")); QUrl url = Utility::concatUrlPath(account()->url(), path(), query); - sendRequest(_usePOST ? "POST" : "GET", url, _request); + const auto httpVerb = verbToString(); + if (!_body.isEmpty()) { + sendRequest(httpVerb, url, _request, _body); + } else { + sendRequest(httpVerb, url, _request); + } AbstractNetworkJob::start(); } diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h index 45fb12a22..01cfcdedd 100644 --- a/src/libsync/networkjobs.h +++ b/src/libsync/networkjobs.h @@ -22,6 +22,7 @@ #include <QBuffer> #include <QUrlQuery> +#include <QJsonDocument> #include <functional> class QUrl; @@ -375,6 +376,13 @@ class OWNCLOUDSYNC_EXPORT JsonApiJob : public AbstractNetworkJob { Q_OBJECT public: + enum class Verb { + Get, + Post, + Put, + Delete, + }; + explicit JsonApiJob(const AccountPtr &account, const QString &path, QObject *parent = nullptr); /** @@ -390,15 +398,9 @@ public: void addQueryParams(const QUrlQuery ¶ms); void addRawHeader(const QByteArray &headerName, const QByteArray &value); - /** - * @brief usePOST - allow job to do an anonymous POST request instead of GET - * @param params: (optional) true for POST, false for GET (default). - * - * This function needs to be called before start() obviously. - */ - void usePOST(bool usePOST = true) { - _usePOST = usePOST; - } + void setBody(const QJsonDocument &body); + + void setVerb(Verb value); public slots: void start() override; @@ -429,10 +431,13 @@ signals: void allowDesktopNotificationsChanged(bool isAllowed); private: + QByteArray _body; QUrlQuery _additionalParams; QNetworkRequest _request; - bool _usePOST = false; + Verb _verb = Verb::Get; + + QByteArray verbToString() const; }; /** diff --git a/src/libsync/ocsuserstatusconnector.cpp b/src/libsync/ocsuserstatusconnector.cpp new file mode 100644 index 000000000..95f3810e2 --- /dev/null +++ b/src/libsync/ocsuserstatusconnector.cpp @@ -0,0 +1,455 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#include "ocsuserstatusconnector.h" +#include "account.h" +#include "userstatusconnector.h" + +#include <networkjobs.h> + +#include <QDateTime> +#include <QtGlobal> +#include <QJsonDocument> +#include <QJsonValue> +#include <QLoggingCategory> +#include <QString> +#include <QJsonObject> +#include <QJsonArray> +#include <qdatetime.h> +#include <qjsonarray.h> +#include <qjsonobject.h> +#include <qloggingcategory.h> + +namespace { + +Q_LOGGING_CATEGORY(lcOcsUserStatusConnector, "nextcloud.gui.ocsuserstatusconnector", QtInfoMsg) + +OCC::UserStatus::OnlineStatus stringToUserOnlineStatus(const QString &status) +{ + // it needs to match the Status enum + const QHash<QString, OCC::UserStatus::OnlineStatus> preDefinedStatus { + { "online", OCC::UserStatus::OnlineStatus::Online }, + { "dnd", OCC::UserStatus::OnlineStatus::DoNotDisturb }, + { "away", OCC::UserStatus::OnlineStatus::Away }, + { "offline", OCC::UserStatus::OnlineStatus::Offline }, + { "invisible", OCC::UserStatus::OnlineStatus::Invisible } + }; + + // api should return invisible, dnd,... toLower() it is to make sure + // it matches _preDefinedStatus, otherwise the default is online (0) + return preDefinedStatus.value(status.toLower(), OCC::UserStatus::OnlineStatus::Online); +} + +QString onlineStatusToString(OCC::UserStatus::OnlineStatus status) +{ + switch (status) { + case OCC::UserStatus::OnlineStatus::Online: + return QStringLiteral("online"); + case OCC::UserStatus::OnlineStatus::DoNotDisturb: + return QStringLiteral("dnd"); + case OCC::UserStatus::OnlineStatus::Away: + return QStringLiteral("offline"); + case OCC::UserStatus::OnlineStatus::Offline: + return QStringLiteral("offline"); + case OCC::UserStatus::OnlineStatus::Invisible: + return QStringLiteral("invisible"); + } + return QStringLiteral("online"); +} + +OCC::Optional<OCC::ClearAt> jsonExtractClearAt(QJsonObject jsonObject) +{ + OCC::Optional<OCC::ClearAt> clearAt {}; + if (jsonObject.contains("clearAt") && !jsonObject.value("clearAt").isNull()) { + OCC::ClearAt clearAtValue; + clearAtValue._type = OCC::ClearAtType::Timestamp; + clearAtValue._timestamp = jsonObject.value("clearAt").toInt(); + clearAt = clearAtValue; + } + return clearAt; +} + +OCC::UserStatus jsonExtractUserStatus(QJsonObject json) +{ + const auto clearAt = jsonExtractClearAt(json); + + const OCC::UserStatus userStatus(json.value("messageId").toString(), + json.value("message").toString().trimmed(), + json.value("icon").toString().trimmed(), stringToUserOnlineStatus(json.value("status").toString()), + json.value("messageIsPredefined").toBool(false), clearAt); + + return userStatus; +} + +OCC::UserStatus jsonToUserStatus(const QJsonDocument &json) +{ + const QJsonObject defaultValues { + { "icon", "" }, + { "message", "" }, + { "status", "online" }, + { "messageIsPredefined", "false" }, + { "statusIsUserDefined", "false" } + }; + const auto retrievedData = json.object().value("ocs").toObject().value("data").toObject(defaultValues); + return jsonExtractUserStatus(retrievedData); +} + +quint64 clearAtEndOfToTimestamp(const OCC::ClearAt &clearAt) +{ + Q_ASSERT(clearAt._type == OCC::ClearAtType::EndOf); + + if (clearAt._endof == "day") { + return QDate::currentDate().addDays(1).startOfDay().toTime_t(); + } else if (clearAt._endof == "week") { + const auto days = Qt::Sunday - QDate::currentDate().dayOfWeek(); + return QDate::currentDate().addDays(days + 1).startOfDay().toTime_t(); + } + qCWarning(lcOcsUserStatusConnector) << "Can not handle clear at endof day type" << clearAt._endof; + return QDateTime::currentDateTime().toTime_t(); +} + +quint64 clearAtPeriodToTimestamp(const OCC::ClearAt &clearAt) +{ + return QDateTime::currentDateTime().addSecs(clearAt._period).toTime_t(); +} + +quint64 clearAtToTimestamp(const OCC::ClearAt &clearAt) +{ + switch (clearAt._type) { + case OCC::ClearAtType::Period: { + return clearAtPeriodToTimestamp(clearAt); + } + + case OCC::ClearAtType::EndOf: { + return clearAtEndOfToTimestamp(clearAt); + } + + case OCC::ClearAtType::Timestamp: { + return clearAt._timestamp; + } + } + + return 0; +} + +quint64 clearAtToTimestamp(const OCC::Optional<OCC::ClearAt> &clearAt) +{ + if (clearAt) { + return clearAtToTimestamp(*clearAt); + } + return 0; +} + +OCC::Optional<OCC::ClearAt> jsonToClearAt(QJsonObject jsonObject) +{ + OCC::Optional<OCC::ClearAt> clearAt; + + if (jsonObject.value("clearAt").isObject() && !jsonObject.value("clearAt").isNull()) { + OCC::ClearAt clearAtValue; + const auto clearAtObject = jsonObject.value("clearAt").toObject(); + const auto typeValue = clearAtObject.value("type").toString("period"); + if (typeValue == "period") { + const auto timeValue = clearAtObject.value("time").toInt(0); + clearAtValue._type = OCC::ClearAtType::Period; + clearAtValue._period = timeValue; + } else if (typeValue == "end-of") { + const auto timeValue = clearAtObject.value("time").toString("day"); + clearAtValue._type = OCC::ClearAtType::EndOf; + clearAtValue._endof = timeValue; + } else { + qCWarning(lcOcsUserStatusConnector) << "Can not handle clear type value" << typeValue; + } + clearAt = clearAtValue; + } + + return clearAt; +} + +OCC::UserStatus jsonToUserStatus(QJsonObject jsonObject) +{ + const auto clearAt = jsonToClearAt(jsonObject); + + OCC::UserStatus userStatus( + jsonObject.value("id").toString("no-id"), + jsonObject.value("message").toString("No message"), + jsonObject.value("icon").toString("no-icon"), + OCC::UserStatus::OnlineStatus::Online, + true, + clearAt); + + return userStatus; +} + +std::vector<OCC::UserStatus> jsonToPredefinedStatuses(QJsonArray jsonDataArray) +{ + std::vector<OCC::UserStatus> statuses; + for (const auto &jsonEntry : jsonDataArray) { + Q_ASSERT(jsonEntry.isObject()); + if (!jsonEntry.isObject()) { + continue; + } + statuses.push_back(jsonToUserStatus(jsonEntry.toObject())); + } + + return statuses; +} + + +const QString baseUrl("/ocs/v2.php/apps/user_status/api/v1"); +const QString userStatusBaseUrl = baseUrl + QStringLiteral("/user_status"); +} + +namespace OCC { + +OcsUserStatusConnector::OcsUserStatusConnector(AccountPtr account, QObject *parent) + : UserStatusConnector(parent) + , _account(account) +{ + Q_ASSERT(_account); + _userStatusSupported = _account->capabilities().userStatus(); + _userStatusEmojisSupported = _account->capabilities().userStatusSupportsEmoji(); +} + +void OcsUserStatusConnector::fetchUserStatus() +{ + qCDebug(lcOcsUserStatusConnector) << "Try to fetch user status"; + + if (!_userStatusSupported) { + qCDebug(lcOcsUserStatusConnector) << "User status not supported"; + emit error(Error::UserStatusNotSupported); + return; + } + + startFetchUserStatusJob(); +} + +void OcsUserStatusConnector::startFetchUserStatusJob() +{ + if (_getUserStatusJob) { + qCDebug(lcOcsUserStatusConnector) << "Get user status job is already running."; + return; + } + + _getUserStatusJob = new JsonApiJob(_account, userStatusBaseUrl, this); + connect(_getUserStatusJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusFetched); + _getUserStatusJob->start(); +} + +void OcsUserStatusConnector::onUserStatusFetched(const QJsonDocument &json, int statusCode) +{ + logResponse("user status fetched", json, statusCode); + + if (statusCode != 200) { + qCInfo(lcOcsUserStatusConnector) << "Slot fetch UserStatus finished with status code" << statusCode; + emit error(Error::CouldNotFetchUserStatus); + return; + } + + _userStatus = jsonToUserStatus(json); + emit userStatusFetched(_userStatus); +} + +void OcsUserStatusConnector::startFetchPredefinedStatuses() +{ + if (_getPredefinedStausesJob) { + qCDebug(lcOcsUserStatusConnector) << "Get predefined statuses job is already running"; + return; + } + + _getPredefinedStausesJob = new JsonApiJob(_account, + baseUrl + QStringLiteral("/predefined_statuses"), this); + connect(_getPredefinedStausesJob, &JsonApiJob::jsonReceived, this, + &OcsUserStatusConnector::onPredefinedStatusesFetched); + _getPredefinedStausesJob->start(); +} + +void OcsUserStatusConnector::fetchPredefinedStatuses() +{ + if (!_userStatusSupported) { + emit error(Error::UserStatusNotSupported); + return; + } + startFetchPredefinedStatuses(); +} + +void OcsUserStatusConnector::onPredefinedStatusesFetched(const QJsonDocument &json, int statusCode) +{ + logResponse("predefined statuses", json, statusCode); + + if (statusCode != 200) { + qCInfo(lcOcsUserStatusConnector) << "Slot predefined user statuses finished with status code" << statusCode; + emit error(Error::CouldNotFetchPredefinedUserStatuses); + return; + } + const auto jsonData = json.object().value("ocs").toObject().value("data"); + Q_ASSERT(jsonData.isArray()); + if (!jsonData.isArray()) { + return; + } + const auto statuses = jsonToPredefinedStatuses(jsonData.toArray()); + emit predefinedStatusesFetched(statuses); +} + +void OcsUserStatusConnector::logResponse(const QString &message, const QJsonDocument &json, int statusCode) +{ + qCDebug(lcOcsUserStatusConnector) << "Response from:" << message << "Status:" << statusCode << "Json:" << json; +} + +void OcsUserStatusConnector::setUserStatusOnlineStatus(UserStatus::OnlineStatus onlineStatus) +{ + _setOnlineStatusJob = new JsonApiJob(_account, + userStatusBaseUrl + QStringLiteral("/status"), this); + _setOnlineStatusJob->setVerb(JsonApiJob::Verb::Put); + // Set body + QJsonObject dataObject; + dataObject.insert("statusType", onlineStatusToString(onlineStatus)); + QJsonDocument body; + body.setObject(dataObject); + _setOnlineStatusJob->setBody(body); + connect(_setOnlineStatusJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusOnlineStatusSet); + _setOnlineStatusJob->start(); +} + +void OcsUserStatusConnector::setUserStatusMessagePredefined(const UserStatus &userStatus) +{ + Q_ASSERT(userStatus.messagePredefined()); + if (!userStatus.messagePredefined()) { + return; + } + + _setMessageJob = new JsonApiJob(_account, userStatusBaseUrl + QStringLiteral("/message/predefined"), this); + _setMessageJob->setVerb(JsonApiJob::Verb::Put); + // Set body + QJsonObject dataObject; + dataObject.insert("messageId", userStatus.id()); + if (userStatus.clearAt()) { + dataObject.insert("clearAt", static_cast<int>(clearAtToTimestamp(userStatus.clearAt()))); + } else { + dataObject.insert("clearAt", QJsonValue()); + } + QJsonDocument body; + body.setObject(dataObject); + _setMessageJob->setBody(body); + connect(_setMessageJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusMessageSet); + _setMessageJob->start(); +} + +void OcsUserStatusConnector::setUserStatusMessageCustom(const UserStatus &userStatus) +{ + Q_ASSERT(!userStatus.messagePredefined()); + if (userStatus.messagePredefined()) { + return; + } + + if (!_userStatusEmojisSupported) { + emit error(Error::EmojisNotSupported); + return; + } + _setMessageJob = new JsonApiJob(_account, userStatusBaseUrl + QStringLiteral("/message/custom"), this); + _setMessageJob->setVerb(JsonApiJob::Verb::Put); + // Set body + QJsonObject dataObject; + dataObject.insert("statusIcon", userStatus.icon()); + dataObject.insert("message", userStatus.message()); + const auto clearAt = userStatus.clearAt(); + if (clearAt) { + dataObject.insert("clearAt", static_cast<int>(clearAtToTimestamp(*clearAt))); + } else { + dataObject.insert("clearAt", QJsonValue()); + } + QJsonDocument body; + body.setObject(dataObject); + _setMessageJob->setBody(body); + connect(_setMessageJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusMessageSet); + _setMessageJob->start(); +} + +void OcsUserStatusConnector::setUserStatusMessage(const UserStatus &userStatus) +{ + if (userStatus.messagePredefined()) { + setUserStatusMessagePredefined(userStatus); + return; + } + setUserStatusMessageCustom(userStatus); +} + +void OcsUserStatusConnector::setUserStatus(const UserStatus &userStatus) +{ + if (!_userStatusSupported) { + emit error(Error::UserStatusNotSupported); + return; + } + + if (_setOnlineStatusJob || _setMessageJob) { + qCDebug(lcOcsUserStatusConnector) << "Set online status job or set message job are already running."; + return; + } + + setUserStatusOnlineStatus(userStatus.state()); + setUserStatusMessage(userStatus); +} + +void OcsUserStatusConnector::onUserStatusOnlineStatusSet(const QJsonDocument &json, int statusCode) +{ + logResponse("Online status set", json, statusCode); + + if (statusCode != 200) { + emit error(Error::CouldNotSetUserStatus); + return; + } +} + +void OcsUserStatusConnector::onUserStatusMessageSet(const QJsonDocument &json, int statusCode) +{ + logResponse("Message set", json, statusCode); + + if (statusCode != 200) { + emit error(Error::CouldNotSetUserStatus); + return; + } + + // We fetch the user status again because json does not contain + // the new message when user status was set from a predefined + // message + fetchUserStatus(); + + emit userStatusSet(); +} + +void OcsUserStatusConnector::clearMessage() +{ + _clearMessageJob = new JsonApiJob(_account, userStatusBaseUrl + QStringLiteral("/message")); + _clearMessageJob->setVerb(JsonApiJob::Verb::Delete); + connect(_clearMessageJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onMessageCleared); + _clearMessageJob->start(); +} + +UserStatus OcsUserStatusConnector::userStatus() const +{ + return _userStatus; +} + +void OcsUserStatusConnector::onMessageCleared(const QJsonDocument &json, int statusCode) +{ + logResponse("Message cleared", json, statusCode); + + if (statusCode != 200) { + emit error(Error::CouldNotClearMessage); + return; + } + + _userStatus = {}; + emit messageCleared(); +} +} diff --git a/src/libsync/ocsuserstatusconnector.h b/src/libsync/ocsuserstatusconnector.h new file mode 100644 index 000000000..0d366419f --- /dev/null +++ b/src/libsync/ocsuserstatusconnector.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#pragma once + +#include "accountfwd.h" +#include "userstatusconnector.h" + +#include <QPointer> + +namespace OCC { + +class JsonApiJob; +class SimpleNetworkJob; + +class OWNCLOUDSYNC_EXPORT OcsUserStatusConnector : public UserStatusConnector +{ +public: + explicit OcsUserStatusConnector(AccountPtr account, QObject *parent = nullptr); + + void fetchUserStatus() override; + + void fetchPredefinedStatuses() override; + + void setUserStatus(const UserStatus &userStatus) override; + + void clearMessage() override; + + UserStatus userStatus() const override; + +private: + void onUserStatusFetched(const QJsonDocument &json, int statusCode); + void onPredefinedStatusesFetched(const QJsonDocument &json, int statusCode); + void onUserStatusOnlineStatusSet(const QJsonDocument &json, int statusCode); + void onUserStatusMessageSet(const QJsonDocument &json, int statusCode); + void onMessageCleared(const QJsonDocument &json, int statusCode); + + void logResponse(const QString &message, const QJsonDocument &json, int statusCode); + void startFetchUserStatusJob(); + void startFetchPredefinedStatuses(); + void setUserStatusOnlineStatus(UserStatus::OnlineStatus onlineStatus); + void setUserStatusMessage(const UserStatus &userStatus); + void setUserStatusMessagePredefined(const UserStatus &userStatus); + void setUserStatusMessageCustom(const UserStatus &userStatus); + + AccountPtr _account; + + bool _userStatusSupported = false; + bool _userStatusEmojisSupported = false; + + QPointer<JsonApiJob> _clearMessageJob {}; + QPointer<JsonApiJob> _setMessageJob {}; + QPointer<JsonApiJob> _setOnlineStatusJob {}; + QPointer<JsonApiJob> _getPredefinedStausesJob {}; + QPointer<JsonApiJob> _getUserStatusJob {}; + + UserStatus _userStatus; +}; +} diff --git a/src/libsync/userstatusconnector.cpp b/src/libsync/userstatusconnector.cpp new file mode 100644 index 000000000..178e8d794 --- /dev/null +++ b/src/libsync/userstatusconnector.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#include "userstatusconnector.h" +#include "theme.h" + +namespace OCC { + +UserStatus::UserStatus() = default; + +UserStatus::UserStatus( + const QString &id, const QString &message, const QString &icon, + OnlineStatus state, bool messagePredefined, const Optional<ClearAt> &clearAt) + : _id(id) + , _message(message) + , _icon(icon) + , _state(state) + , _messagePredefined(messagePredefined) + , _clearAt(clearAt) +{ +} + +QString UserStatus::id() const +{ + return _id; +} + +QString UserStatus::message() const +{ + return _message; +} + +QString UserStatus::icon() const +{ + return _icon; +} + +auto UserStatus::state() const -> OnlineStatus +{ + return _state; +} + +bool UserStatus::messagePredefined() const +{ + return _messagePredefined; +} + +QUrl UserStatus::stateIcon() const +{ + switch (_state) { + case UserStatus::OnlineStatus::Away: + return Theme::instance()->statusAwayImageSource(); + + case UserStatus::OnlineStatus::DoNotDisturb: + return Theme::instance()->statusDoNotDisturbImageSource(); + + case UserStatus::OnlineStatus::Invisible: + case UserStatus::OnlineStatus::Offline: + return Theme::instance()->statusInvisibleImageSource(); + + case UserStatus::OnlineStatus::Online: + return Theme::instance()->statusOnlineImageSource(); + } + + Q_UNREACHABLE(); +} + +Optional<ClearAt> UserStatus::clearAt() const +{ + return _clearAt; +} + +void UserStatus::setId(const QString &id) +{ + _id = id; +} + +void UserStatus::setMessage(const QString &message) +{ + _message = message; +} + +void UserStatus::setState(OnlineStatus state) +{ + _state = state; +} + +void UserStatus::setIcon(const QString &icon) +{ + _icon = icon; +} + +void UserStatus::setMessagePredefined(bool value) +{ + _messagePredefined = value; +} + +void UserStatus::setClearAt(const Optional<ClearAt> &dateTime) +{ + _clearAt = dateTime; +} + + +UserStatusConnector::UserStatusConnector(QObject *parent) + : QObject(parent) +{ +} + +UserStatusConnector::~UserStatusConnector() = default; +} diff --git a/src/libsync/userstatusconnector.h b/src/libsync/userstatusconnector.h new file mode 100644 index 000000000..d5593fe9e --- /dev/null +++ b/src/libsync/userstatusconnector.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#pragma once + +#include "common/result.h" +#include "owncloudlib.h" + +#include <QObject> +#include <QString> +#include <QMetaType> +#include <QUrl> +#include <QDateTime> +#include <QtGlobal> +#include <QVariant> + +#include <vector> + + +namespace OCC { + +enum class OWNCLOUDSYNC_EXPORT ClearAtType { + Period, + EndOf, + Timestamp +}; + +// TODO: If we can use C++17 make it a std::variant +struct OWNCLOUDSYNC_EXPORT ClearAt +{ + ClearAtType _type = ClearAtType::Period; + + quint64 _timestamp; + int _period; + QString _endof; +}; + +class OWNCLOUDSYNC_EXPORT UserStatus +{ + Q_GADGET + + Q_PROPERTY(QString id MEMBER _id) + Q_PROPERTY(QString message MEMBER _message) + Q_PROPERTY(QString icon MEMBER _icon) + Q_PROPERTY(OnlineStatus state MEMBER _state) + +public: + enum class OnlineStatus : quint8 { + Online, + DoNotDisturb, + Away, + Offline, + Invisible + }; + Q_ENUM(OnlineStatus); + + UserStatus(); + + UserStatus(const QString &id, const QString &message, const QString &icon, + OnlineStatus state, bool messagePredefined, const Optional<ClearAt> &clearAt = {}); + + Q_REQUIRED_RESULT QString id() const; + Q_REQUIRED_RESULT QString message() const; + Q_REQUIRED_RESULT QString icon() const; + Q_REQUIRED_RESULT OnlineStatus state() const; + Q_REQUIRED_RESULT Optional<ClearAt> clearAt() const; + + void setId(const QString &id); + void setMessage(const QString &message); + void setState(OnlineStatus state); + void setIcon(const QString &icon); + void setMessagePredefined(bool value); + void setClearAt(const Optional<ClearAt> &dateTime); + + Q_REQUIRED_RESULT bool messagePredefined() const; + + Q_REQUIRED_RESULT QUrl stateIcon() const; + +private: + QString _id; + QString _message; + QString _icon; + OnlineStatus _state = OnlineStatus::Online; + bool _messagePredefined; + Optional<ClearAt> _clearAt; +}; + +class OWNCLOUDSYNC_EXPORT UserStatusConnector : public QObject +{ + Q_OBJECT + +public: + enum class Error { + CouldNotFetchUserStatus, + CouldNotFetchPredefinedUserStatuses, + UserStatusNotSupported, + EmojisNotSupported, + CouldNotSetUserStatus, + CouldNotClearMessage + }; + Q_ENUM(Error) + + explicit UserStatusConnector(QObject *parent = nullptr); + + ~UserStatusConnector() override; + + virtual void fetchUserStatus() = 0; + + virtual void fetchPredefinedStatuses() = 0; + + virtual void setUserStatus(const UserStatus &userStatus) = 0; + + virtual void clearMessage() = 0; + + virtual UserStatus userStatus() const = 0; + +signals: + void userStatusFetched(const UserStatus &userStatus); + void predefinedStatusesFetched(const std::vector<UserStatus> &statuses); + void userStatusSet(); + void messageCleared(); + void error(Error error); +}; +} + +Q_DECLARE_METATYPE(OCC::UserStatusConnector *) +Q_DECLARE_METATYPE(OCC::UserStatus) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 924520941..1197d1cbd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,6 +59,7 @@ nextcloud_add_test(PushNotifications) nextcloud_add_test(Theme) nextcloud_add_test(IconUtils) nextcloud_add_test(NotificationCache) +nextcloud_add_test(SetUserStatusDialog) if( UNIX AND NOT APPLE ) nextcloud_add_test(InotifyWatcher) diff --git a/test/testcapabilities.cpp b/test/testcapabilities.cpp index 1d85d0a37..05259f8f6 100644 --- a/test/testcapabilities.cpp +++ b/test/testcapabilities.cpp @@ -138,6 +138,82 @@ private slots: QCOMPARE(capabilities.pushNotificationsWebSocketUrl(), websocketUrl); } + + void testUserStatus_userStatusAvailable_returnTrue() + { + QVariantMap userStatusMap; + userStatusMap["enabled"] = true; + + QVariantMap capabilitiesMap; + capabilitiesMap["user_status"] = userStatusMap; + + const OCC::Capabilities capabilities(capabilitiesMap); + + QVERIFY(capabilities.userStatus()); + } + + void testUserStatus_userStatusNotAvailable_returnFalse() + { + QVariantMap userStatusMap; + userStatusMap["enabled"] = false; + + QVariantMap capabilitiesMap; + capabilitiesMap["user_status"] = userStatusMap; + + const OCC::Capabilities capabilities(capabilitiesMap); + + QVERIFY(!capabilities.userStatus()); + } + + void testUserStatus_userStatusNotInCapabilites_returnFalse() + { + QVariantMap capabilitiesMap; + + const OCC::Capabilities capabilities(capabilitiesMap); + + QVERIFY(!capabilities.userStatus()); + } + + void testUserStatusSupportsEmoji_supportsEmojiAvailable_returnTrue() + { + QVariantMap userStatusMap; + userStatusMap["enabled"] = true; + userStatusMap["supports_emoji"] = true; + + QVariantMap capabilitiesMap; + capabilitiesMap["user_status"] = userStatusMap; + + const OCC::Capabilities capabilities(capabilitiesMap); + + QVERIFY(capabilities.userStatus()); + } + + void testUserStatusSupportsEmoji_supportsEmojiNotAvailable_returnFalse() + { + QVariantMap userStatusMap; + userStatusMap["enabled"] = true; + userStatusMap["supports_emoji"] = false; + + QVariantMap capabilitiesMap; + capabilitiesMap["user_status"] = userStatusMap; + + const OCC::Capabilities capabilities(capabilitiesMap); + + QVERIFY(!capabilities.userStatusSupportsEmoji()); + } + + void testUserStatusSupportsEmoji_supportsEmojiNotInCapabilites_returnFalse() + { + QVariantMap userStatusMap; + userStatusMap["enabled"] = true; + + QVariantMap capabilitiesMap; + capabilitiesMap["user_status"] = userStatusMap; + + const OCC::Capabilities capabilities(capabilitiesMap); + + QVERIFY(!capabilities.userStatusSupportsEmoji()); + } }; QTEST_GUILESS_MAIN(TestCapabilities) diff --git a/test/testpushnotifications.cpp b/test/testpushnotifications.cpp index b7e00583f..9345918e5 100644 --- a/test/testpushnotifications.cpp +++ b/test/testpushnotifications.cpp @@ -65,6 +65,20 @@ class TestPushNotifications : public QObject Q_OBJECT private slots: + void testTryReconnect_capabilitesReportPushNotificationsAvailable_reconnectForEver() + { + FakeWebSocketServer fakeServer; + auto account = FakeWebSocketServer::createAccount(); + account->setPushNotificationsReconnectInterval(0); + + // Let if fail a few times + QVERIFY(failThreeAuthenticationAttempts(fakeServer, account)); + QVERIFY(failThreeAuthenticationAttempts(fakeServer, account)); + + // Push notifications should try to reconnect + QVERIFY(fakeServer.authenticateAccount(account)); + } + void testSetup_correctCredentials_authenticateAndEmitReady() { FakeWebSocketServer fakeServer; @@ -272,20 +286,6 @@ private slots: QVERIFY(verifyCalledOnceWithAccount(*activitiesChangedSpy, account)); })); } - - void testTryReconnect_capabilitesReportPushNotificationsAvailable_reconnectForEver() - { - FakeWebSocketServer fakeServer; - auto account = FakeWebSocketServer::createAccount(); - account->setPushNotificationsReconnectInterval(0); - - // Let if fail a few times - QVERIFY(failThreeAuthenticationAttempts(fakeServer, account)); - QVERIFY(failThreeAuthenticationAttempts(fakeServer, account)); - - // Push notifications should try to reconnect - QVERIFY(fakeServer.authenticateAccount(account)); - } }; QTEST_GUILESS_MAIN(TestPushNotifications) diff --git a/test/testsetuserstatusdialog.cpp b/test/testsetuserstatusdialog.cpp new file mode 100644 index 000000000..045463856 --- /dev/null +++ b/test/testsetuserstatusdialog.cpp @@ -0,0 +1,747 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#include "userstatusconnector.h" +#include "userstatusselectormodel.h" + +#include <QTest> +#include <QSignalSpy> +#include <QDateTime> + +#include <memory> + +class FakeUserStatusConnector : public OCC::UserStatusConnector +{ +public: + void fetchUserStatus() override + { + if (_couldNotFetchUserStatus) { + emit error(Error::CouldNotFetchUserStatus); + return; + } else if (_userStatusNotSupported) { + emit error(Error::UserStatusNotSupported); + return; + } else if (_emojisNotSupported) { + emit error(Error::EmojisNotSupported); + return; + } + + emit userStatusFetched(_userStatus); + } + + void fetchPredefinedStatuses() override + { + if (_couldNotFetchPredefinedUserStatuses) { + emit error(Error::CouldNotFetchPredefinedUserStatuses); + return; + } + emit predefinedStatusesFetched(_predefinedStatuses); + } + + void setUserStatus(const OCC::UserStatus &userStatus) override + { + if (_couldNotSetUserStatusMessage) { + emit error(Error::CouldNotSetUserStatus); + return; + } + + _userStatusSetByCallerOfSetUserStatus = userStatus; + emit UserStatusConnector::userStatusSet(); + } + + void clearMessage() override + { + if (_couldNotClearUserStatusMessage) { + emit error(Error::CouldNotClearMessage); + } else { + _isMessageCleared = true; + } + } + + OCC::UserStatus userStatus() const override + { + return {}; // Not implemented + } + + void setFakeUserStatus(const OCC::UserStatus &userStatus) + { + _userStatus = userStatus; + } + + void setFakePredefinedStatuses( + const std::vector<OCC::UserStatus> &statuses) + { + _predefinedStatuses = statuses; + } + + OCC::UserStatus userStatusSetByCallerOfSetUserStatus() const { return _userStatusSetByCallerOfSetUserStatus; } + + bool messageCleared() const { return _isMessageCleared; } + + void setErrorCouldNotFetchPredefinedUserStatuses(bool value) + { + _couldNotFetchPredefinedUserStatuses = value; + } + + void setErrorCouldNotFetchUserStatus(bool value) + { + _couldNotFetchUserStatus = value; + } + + void setErrorCouldNotSetUserStatusMessage(bool value) + { + _couldNotSetUserStatusMessage = value; + } + + void setErrorUserStatusNotSupported(bool value) + { + _userStatusNotSupported = value; + } + + void setErrorEmojisNotSupported(bool value) + { + _emojisNotSupported = value; + } + + void setErrorCouldNotClearUserStatusMessage(bool value) + { + _couldNotClearUserStatusMessage = value; + } + +private: + OCC::UserStatus _userStatusSetByCallerOfSetUserStatus; + OCC::UserStatus _userStatus; + std::vector<OCC::UserStatus> _predefinedStatuses; + bool _isMessageCleared = false; + bool _couldNotFetchPredefinedUserStatuses = false; + bool _couldNotFetchUserStatus = false; + bool _couldNotSetUserStatusMessage = false; + bool _userStatusNotSupported = false; + bool _emojisNotSupported = false; + bool _couldNotClearUserStatusMessage = false; +}; + +class FakeDateTimeProvider : public OCC::DateTimeProvider +{ +public: + void setCurrentDateTime(const QDateTime &dateTime) { _dateTime = dateTime; } + + QDateTime currentDateTime() const override { return _dateTime; } + + QDate currentDate() const override { return _dateTime.date(); } + +private: + QDateTime _dateTime; +}; + +static std::vector<OCC::UserStatus> +createFakePredefinedStatuses(const QDateTime ¤tTime) +{ + std::vector<OCC::UserStatus> statuses; + + const QString userStatusId("fake-id"); + const QString userStatusMessage("Predefined status"); + const QString userStatusIcon("🏖"); + const OCC::UserStatus::OnlineStatus userStatusState(OCC::UserStatus::OnlineStatus::Online); + const bool userStatusMessagePredefined(true); + OCC::Optional<OCC::ClearAt> userStatusClearAt; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addSecs(60 * 60).toTime_t(); + userStatusClearAt = clearAt; + + statuses.emplace_back(userStatusId, userStatusMessage, userStatusIcon, + userStatusState, userStatusMessagePredefined, userStatusClearAt); + + return statuses; +} + +static QDateTime createDateTime(int year = 2021, int month = 7, int day = 27, + int hour = 12, int minute = 0, int second = 0) +{ + QDate fakeDate(year, month, day); + QTime fakeTime(hour, minute, second); + QDateTime fakeDateTime; + + fakeDateTime.setDate(fakeDate); + fakeDateTime.setTime(fakeTime); + + return fakeDateTime; +} + +class TestSetUserStatusDialog : public QObject +{ + Q_OBJECT + +private slots: + void testCtor_fetchStatusAndPredefinedStatuses() + { + const QDateTime currentDateTime(QDateTime::currentDateTime()); + + const QString userStatusId("fake-id"); + const QString userStatusMessage("Some status"); + const QString userStatusIcon("❤"); + const OCC::UserStatus::OnlineStatus userStatusState(OCC::UserStatus::OnlineStatus::DoNotDisturb); + const bool userStatusMessagePredefined(false); + OCC::Optional<OCC::ClearAt> userStatusClearAt; + { + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentDateTime.addDays(1).toTime_t(); + userStatusClearAt = clearAt; + } + + const OCC::UserStatus userStatus(userStatusId, userStatusMessage, + userStatusIcon, userStatusState, userStatusMessagePredefined, userStatusClearAt); + + const auto fakePredefinedStatuses = createFakePredefinedStatuses(createDateTime()); + + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentDateTime); + fakeUserStatusJob->setFakeUserStatus(userStatus); + fakeUserStatusJob->setFakePredefinedStatuses(fakePredefinedStatuses); + OCC::UserStatusSelectorModel model(fakeUserStatusJob, std::move(fakeDateTimeProvider)); + + // Was user status set correctly? + QCOMPARE(model.userStatusMessage(), userStatusMessage); + QCOMPARE(model.userStatusEmoji(), userStatusIcon); + QCOMPARE(model.onlineStatus(), userStatusState); + QCOMPARE(model.clearAt(), tr("1 day")); + + // Were predefined statuses fetched correctly? + const auto predefinedStatusesCount = model.predefinedStatusesCount(); + QCOMPARE(predefinedStatusesCount, fakePredefinedStatuses.size()); + for (int i = 0; i < predefinedStatusesCount; ++i) { + const auto predefinedStatus = model.predefinedStatus(i); + QCOMPARE(predefinedStatus.id(), + fakePredefinedStatuses[i].id()); + QCOMPARE(predefinedStatus.message(), + fakePredefinedStatuses[i].message()); + QCOMPARE(predefinedStatus.icon(), + fakePredefinedStatuses[i].icon()); + QCOMPARE(predefinedStatus.messagePredefined(), + fakePredefinedStatuses[i].messagePredefined()); + } + } + + void testCtor_noStatusSet_showSensibleDefaults() + { + OCC::UserStatusSelectorModel model(nullptr, nullptr); + + QCOMPARE(model.userStatusMessage(), ""); + QCOMPARE(model.userStatusEmoji(), "😀"); + QCOMPARE(model.clearAt(), tr("Don't clear")); + } + + void testCtor_fetchStatusButNoStatusSet_showSensibleDefaults() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setFakeUserStatus({ "", "", "", + OCC::UserStatus::OnlineStatus::Offline, false, {} }); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + QCOMPARE(model.onlineStatus(), OCC::UserStatus::OnlineStatus::Online); + QCOMPARE(model.userStatusMessage(), ""); + QCOMPARE(model.userStatusEmoji(), "😀"); + QCOMPARE(model.clearAt(), tr("Don't clear")); + } + + void testSetOnlineStatus_emitOnlineStatusChanged() + { + const OCC::UserStatus::OnlineStatus onlineStatus(OCC::UserStatus::OnlineStatus::Invisible); + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy onlineStatusChangedSpy(&model, + &OCC::UserStatusSelectorModel::onlineStatusChanged); + + model.setOnlineStatus(onlineStatus); + + QCOMPARE(onlineStatusChangedSpy.count(), 1); + } + + void testSetUserStatus_setCustomMessage_userStatusSetCorrect() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy finishedSpy(&model, &OCC::UserStatusSelectorModel::finished); + + const QString userStatusMessage("Some status"); + const QString userStatusIcon("❤"); + const OCC::UserStatus::OnlineStatus userStatusState(OCC::UserStatus::OnlineStatus::Online); + + model.setOnlineStatus(userStatusState); + model.setUserStatusMessage(userStatusMessage); + model.setUserStatusEmoji(userStatusIcon); + model.setClearAt(1); + + model.setUserStatus(); + QCOMPARE(finishedSpy.count(), 1); + + const auto userStatusSet = fakeUserStatusJob->userStatusSetByCallerOfSetUserStatus(); + QCOMPARE(userStatusSet.icon(), userStatusIcon); + QCOMPARE(userStatusSet.message(), userStatusMessage); + QCOMPARE(userStatusSet.state(), userStatusState); + QCOMPARE(userStatusSet.messagePredefined(), false); + const auto clearAt = userStatusSet.clearAt(); + QVERIFY(clearAt.isValid()); + QCOMPARE(clearAt->_type, OCC::ClearAtType::Period); + QCOMPARE(clearAt->_period, 60 * 30); + } + + void testSetUserStatusMessage_predefinedStatusWasSet_userStatusSetCorrect() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setFakePredefinedStatuses(createFakePredefinedStatuses(createDateTime())); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + model.setPredefinedStatus(0); + QSignalSpy finishedSpy(&model, &OCC::UserStatusSelectorModel::finished); + + const QString userStatusMessage("Some status"); + const OCC::UserStatus::OnlineStatus userStatusState(OCC::UserStatus::OnlineStatus::Online); + + model.setOnlineStatus(userStatusState); + model.setUserStatusMessage(userStatusMessage); + model.setClearAt(1); + + model.setUserStatus(); + QCOMPARE(finishedSpy.count(), 1); + + const auto userStatusSet = fakeUserStatusJob->userStatusSetByCallerOfSetUserStatus(); + QCOMPARE(userStatusSet.message(), userStatusMessage); + QCOMPARE(userStatusSet.state(), userStatusState); + QCOMPARE(userStatusSet.messagePredefined(), false); + const auto clearAt = userStatusSet.clearAt(); + QVERIFY(clearAt.isValid()); + QCOMPARE(clearAt->_type, OCC::ClearAtType::Period); + QCOMPARE(clearAt->_period, 60 * 30); + } + + void testSetUserStatusEmoji_predefinedStatusWasSet_userStatusSetCorrect() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setFakePredefinedStatuses(createFakePredefinedStatuses(createDateTime())); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + model.setPredefinedStatus(0); + QSignalSpy finishedSpy(&model, &OCC::UserStatusSelectorModel::finished); + + const QString userStatusIcon("❤"); + const OCC::UserStatus::OnlineStatus userStatusState(OCC::UserStatus::OnlineStatus::Online); + + model.setOnlineStatus(userStatusState); + model.setUserStatusEmoji(userStatusIcon); + model.setClearAt(1); + + model.setUserStatus(); + QCOMPARE(finishedSpy.count(), 1); + + const auto userStatusSet = fakeUserStatusJob->userStatusSetByCallerOfSetUserStatus(); + QCOMPARE(userStatusSet.icon(), userStatusIcon); + QCOMPARE(userStatusSet.state(), userStatusState); + QCOMPARE(userStatusSet.messagePredefined(), false); + const auto clearAt = userStatusSet.clearAt(); + QVERIFY(clearAt.isValid()); + QCOMPARE(clearAt->_type, OCC::ClearAtType::Period); + QCOMPARE(clearAt->_period, 60 * 30); + } + + void testSetPredefinedStatus_emitUserStatusChangedAndSetUserStatus() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + const auto currentTime = createDateTime(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + const auto fakePredefinedStatuses = createFakePredefinedStatuses(currentTime); + fakeUserStatusJob->setFakePredefinedStatuses(fakePredefinedStatuses); + OCC::UserStatusSelectorModel model(std::move(fakeUserStatusJob), + std::move(fakeDateTimeProvider)); + + QSignalSpy userStatusChangedSpy(&model, + &OCC::UserStatusSelectorModel::userStatusChanged); + QSignalSpy clearAtChangedSpy(&model, + &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto fakePredefinedUserStatusIndex = 0; + model.setPredefinedStatus(fakePredefinedUserStatusIndex); + + QCOMPARE(userStatusChangedSpy.count(), 1); + QCOMPARE(clearAtChangedSpy.count(), 1); + + // Was user status set correctly? + const auto fakePredefinedUserStatus = fakePredefinedStatuses[fakePredefinedUserStatusIndex]; + QCOMPARE(model.userStatusMessage(), fakePredefinedUserStatus.message()); + QCOMPARE(model.userStatusEmoji(), fakePredefinedUserStatus.icon()); + QCOMPARE(model.onlineStatus(), fakePredefinedUserStatus.state()); + QCOMPARE(model.clearAt(), tr("1 hour")); + } + + void testSetClear_setClearAtStage0_emitClearAtChangedAndClearAtSet() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy clearAtChangedSpy(&model, &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto clearAtIndex = 0; + model.setClearAt(clearAtIndex); + + QCOMPARE(clearAtChangedSpy.count(), 1); + QCOMPARE(model.clearAt(), tr("Don't clear")); + } + + void testSetClear_setClearAtStage1_emitClearAtChangedAndClearAtSet() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy clearAtChangedSpy(&model, &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto clearAtIndex = 1; + model.setClearAt(clearAtIndex); + + QCOMPARE(clearAtChangedSpy.count(), 1); + QCOMPARE(model.clearAt(), tr("30 minutes")); + } + + void testSetClear_setClearAtStage2_emitClearAtChangedAndClearAtSet() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy clearAtChangedSpy(&model, &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto clearAtIndex = 2; + model.setClearAt(clearAtIndex); + + QCOMPARE(clearAtChangedSpy.count(), 1); + QCOMPARE(model.clearAt(), tr("1 hour")); + } + + void testSetClear_setClearAtStage3_emitClearAtChangedAndClearAtSet() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy clearAtChangedSpy(&model, &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto clearAtIndex = 3; + model.setClearAt(clearAtIndex); + + QCOMPARE(clearAtChangedSpy.count(), 1); + QCOMPARE(model.clearAt(), tr("4 hours")); + } + + void testSetClear_setClearAtStage4_emitClearAtChangedAndClearAtSet() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy clearAtChangedSpy(&model, &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto clearAtIndex = 4; + model.setClearAt(clearAtIndex); + + QCOMPARE(clearAtChangedSpy.count(), 1); + QCOMPARE(model.clearAt(), tr("Today")); + } + + void testSetClear_setClearAtStage5_emitClearAtChangedAndClearAtSet() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + QSignalSpy clearAtChangedSpy(&model, &OCC::UserStatusSelectorModel::clearAtChanged); + + const auto clearAtIndex = 5; + model.setClearAt(clearAtIndex); + + QCOMPARE(clearAtChangedSpy.count(), 1); + QCOMPARE(model.clearAt(), tr("This week")); + } + + void testClearAtStages() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + QCOMPARE(model.clearAt(), tr("Don't clear")); + const auto clearAtValues = model.clearAtValues(); + QCOMPARE(clearAtValues.count(), 6); + + QCOMPARE(clearAtValues[0], tr("Don't clear")); + QCOMPARE(clearAtValues[1], tr("30 minutes")); + QCOMPARE(clearAtValues[2], tr("1 hour")); + QCOMPARE(clearAtValues[3], tr("4 hours")); + QCOMPARE(clearAtValues[4], tr("Today")); + QCOMPARE(clearAtValues[5], tr("This week")); + } + + void testClearAt_clearAtTimestamp() + { + const auto currentTime = createDateTime(); + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addSecs(30).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("Less than a minute")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addSecs(60).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("1 minute")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addSecs(60 * 30).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("30 minutes")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addSecs(60 * 60).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("1 hour")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addSecs(60 * 60 * 4).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("4 hours")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addDays(1).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("1 day")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Timestamp; + clearAt._timestamp = currentTime.addDays(7).toTime_t(); + userStatus.setClearAt(clearAt); + + auto fakeDateTimeProvider = std::make_unique<FakeDateTimeProvider>(); + fakeDateTimeProvider->setCurrentDateTime(currentTime); + + OCC::UserStatusSelectorModel model(userStatus, std::move(fakeDateTimeProvider)); + + QCOMPARE(model.clearAt(), tr("7 days")); + } + } + + void testClearAt_clearAtEndOf() + { + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::EndOf; + clearAt._endof = "day"; + userStatus.setClearAt(clearAt); + + OCC::UserStatusSelectorModel model(userStatus); + + QCOMPARE(model.clearAt(), tr("Today")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::EndOf; + clearAt._endof = "week"; + userStatus.setClearAt(clearAt); + + OCC::UserStatusSelectorModel model(userStatus); + + QCOMPARE(model.clearAt(), tr("This week")); + } + } + + void testClearAt_clearAtAfterPeriod() + { + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Period; + clearAt._period = 60 * 30; + userStatus.setClearAt(clearAt); + + OCC::UserStatusSelectorModel model(userStatus); + + QCOMPARE(model.clearAt(), tr("30 minutes")); + } + + { + OCC::UserStatus userStatus; + OCC::ClearAt clearAt; + clearAt._type = OCC::ClearAtType::Period; + clearAt._period = 60 * 60; + userStatus.setClearAt(clearAt); + + OCC::UserStatusSelectorModel model(userStatus); + + QCOMPARE(model.clearAt(), tr("1 hour")); + } + } + + void testClearUserStatus() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + model.clearUserStatus(); + + QVERIFY(fakeUserStatusJob->messageCleared()); + } + + void testError_couldNotFetchPredefinedStatuses_emitError() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setErrorCouldNotFetchPredefinedUserStatuses(true); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + QCOMPARE(model.errorMessage(), + tr("Could not fetch predefined statuses. Make sure you are connected to the server.")); + } + + void testError_couldNotFetchUserStatus_emitError() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setErrorCouldNotFetchUserStatus(true); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + QCOMPARE(model.errorMessage(), + tr("Could not fetch user status. Make sure you are connected to the server.")); + } + + void testError_userStatusNotSupported_emitError() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setErrorUserStatusNotSupported(true); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + QCOMPARE(model.errorMessage(), + tr("User status feature is not supported. You will not be able to set your user status.")); + } + + void testError_couldSetUserStatus_emitError() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setErrorCouldNotSetUserStatusMessage(true); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + model.setUserStatus(); + + QCOMPARE(model.errorMessage(), + tr("Could not set user status. Make sure you are connected to the server.")); + } + + void testError_emojisNotSupported_emitError() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setErrorEmojisNotSupported(true); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + QCOMPARE(model.errorMessage(), + tr("Emojis feature is not supported. Some user status functionality may not work.")); + } + + void testError_couldNotClearMessage_emitError() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + fakeUserStatusJob->setErrorCouldNotClearUserStatusMessage(true); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + model.clearUserStatus(); + + QCOMPARE(model.errorMessage(), + tr("Could not clear user status message. Make sure you are connected to the server.")); + } + + void testError_setUserStatus_clearErrorMessage() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + fakeUserStatusJob->setErrorCouldNotSetUserStatusMessage(true); + model.setUserStatus(); + QVERIFY(!model.errorMessage().isEmpty()); + fakeUserStatusJob->setErrorCouldNotSetUserStatusMessage(false); + model.setUserStatus(); + QVERIFY(model.errorMessage().isEmpty()); + } + + void testError_clearUserStatus_clearErrorMessage() + { + auto fakeUserStatusJob = std::make_shared<FakeUserStatusConnector>(); + OCC::UserStatusSelectorModel model(fakeUserStatusJob); + + fakeUserStatusJob->setErrorCouldNotSetUserStatusMessage(true); + model.setUserStatus(); + QVERIFY(!model.errorMessage().isEmpty()); + fakeUserStatusJob->setErrorCouldNotSetUserStatusMessage(false); + model.clearUserStatus(); + QVERIFY(model.errorMessage().isEmpty()); + } +}; + +QTEST_GUILESS_MAIN(TestSetUserStatusDialog) +#include "testsetuserstatusdialog.moc" |