Move recent emoji and variants to common settings.

Fixes #16163, fixes #4018, partially fixes #10123.
This commit is contained in:
John Preston 2021-04-25 12:12:32 +04:00
parent 68e35b232d
commit 5bb73d8d3d
12 changed files with 276 additions and 190 deletions

View file

@ -399,7 +399,7 @@ EmojiListWidget::EmojiListWidget(
for (auto i = 0; i != kEmojiSectionCount; ++i) { for (auto i = 0; i != kEmojiSectionCount; ++i) {
const auto section = static_cast<Section>(i); const auto section = static_cast<Section>(i);
_counts[i] = (section == Section::Recent) _counts[i] = (section == Section::Recent)
? GetRecentEmoji().size() ? int(Core::App().settings().recentEmoji().size())
: Ui::Emoji::GetSectionCount(section); : Ui::Emoji::GetSectionCount(section);
} }
@ -500,17 +500,18 @@ void EmojiListWidget::ensureLoaded(int section) {
return; return;
} }
_emoji[section] = (static_cast<Section>(section) == Section::Recent) _emoji[section] = (static_cast<Section>(section) == Section::Recent)
? GetRecentEmojiSection() ? Core::App().settings().recentEmojiSection()
: Ui::Emoji::GetSection(static_cast<Section>(section)); : Ui::Emoji::GetSection(static_cast<Section>(section));
_counts[section] = _emoji[section].size(); _counts[section] = _emoji[section].size();
if (static_cast<Section>(section) == Section::Recent) { if (static_cast<Section>(section) == Section::Recent) {
return; return;
} }
const auto &variants = Core::App().settings().emojiVariants();
for (auto &emoji : _emoji[section]) { for (auto &emoji : _emoji[section]) {
if (emoji->hasVariants()) { if (emoji->hasVariants()) {
auto j = cEmojiVariants().constFind(emoji->nonColoredId()); const auto j = variants.find(emoji->nonColoredId());
if (j != cEmojiVariants().cend()) { if (j != end(variants)) {
emoji = emoji->variant(j.value()); emoji = emoji->variant(j->second);
} }
} }
} }
@ -594,10 +595,13 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
if (_selected >= 0) { if (_selected >= 0) {
auto section = (_selected / MatrixRowShift); auto section = (_selected / MatrixRowShift);
auto sel = _selected % MatrixRowShift; auto sel = _selected % MatrixRowShift;
if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { if (section < kEmojiSectionCount
&& sel < _emoji[section].size()
&& _emoji[section][sel]->hasVariants()) {
_pickerSel = _selected; _pickerSel = _selected;
setCursor(style::cur_default); setCursor(style::cur_default);
if (!cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { const auto &variants = Core::App().settings().emojiVariants();
if (!variants.contains(_emoji[section][sel]->nonColoredId())) {
showPicker(); showPicker();
} else { } else {
_showPickerTimer.callOnce(500); _showPickerTimer.callOnce(500);
@ -617,8 +621,11 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
} else if (_pickerSel >= 0) { } else if (_pickerSel >= 0) {
auto section = (_pickerSel / MatrixRowShift); auto section = (_pickerSel / MatrixRowShift);
auto sel = _pickerSel % MatrixRowShift; auto sel = _pickerSel % MatrixRowShift;
if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { if (section < kEmojiSectionCount
if (cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { && sel < _emoji[section].size()
&& _emoji[section][sel]->hasVariants()) {
const auto &variants = Core::App().settings().emojiVariants();
if (variants.contains(_emoji[section][sel]->nonColoredId())) {
_picker->hideAnimated(); _picker->hideAnimated();
_pickerSel = -1; _pickerSel = -1;
} }
@ -650,7 +657,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
} }
void EmojiListWidget::selectEmoji(EmojiPtr emoji) { void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
AddRecentEmoji(emoji); Core::App().settings().incrementRecentEmoji(emoji);
_chosen.fire_copy(emoji); _chosen.fire_copy(emoji);
} }
@ -698,10 +705,7 @@ QRect EmojiListWidget::emojiRect(int section, int sel) {
void EmojiListWidget::colorChosen(EmojiPtr emoji) { void EmojiListWidget::colorChosen(EmojiPtr emoji) {
if (emoji->hasVariants()) { if (emoji->hasVariants()) {
cRefEmojiVariants().insert( Core::App().settings().saveEmojiVariant(emoji);
emoji->nonColoredId(),
emoji->variantIndex(emoji));
controller()->session().saveSettingsDelayed();
} }
if (_pickerSel >= 0) { if (_pickerSel >= 0) {
auto section = (_pickerSel / MatrixRowShift); auto section = (_pickerSel / MatrixRowShift);
@ -790,7 +794,7 @@ void EmojiListWidget::processHideFinished() {
void EmojiListWidget::refreshRecent() { void EmojiListWidget::refreshRecent() {
clearSelection(); clearSelection();
_emoji[0] = GetRecentEmojiSection(); _emoji[0] = Core::App().settings().recentEmojiSection();
_counts[0] = _emoji[0].size(); _counts[0] = _emoji[0].size();
resizeToWidth(width()); resizeToWidth(width());
} }

View file

@ -116,11 +116,11 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
}) | ranges::to_vector; }) | ranges::to_vector;
auto lastRecent = begin(result); auto lastRecent = begin(result);
const auto &recent = GetRecentEmoji(); const auto &recent = Core::App().settings().recentEmoji();
for (const auto &item : recent) { for (const auto &item : recent) {
const auto emoji = item.first->original() const auto emoji = item.emoji->original()
? item.first->original() ? item.emoji->original()
: item.first; : item.emoji;
const auto it = ranges::find(result, emoji, [](const Row &row) { const auto it = ranges::find(result, emoji, [](const Row &row) {
return row.emoji.get(); return row.emoji.get();
}); });
@ -133,12 +133,12 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
for (auto &item : result) { for (auto &item : result) {
item.emoji = [&] { item.emoji = [&] {
const auto result = item.emoji; const auto result = item.emoji;
const auto &variants = cEmojiVariants(); const auto &variants = Core::App().settings().emojiVariants();
const auto i = result->hasVariants() const auto i = result->hasVariants()
? variants.constFind(result->nonColoredId()) ? variants.find(result->nonColoredId())
: variants.cend(); : end(variants);
return (i != variants.cend()) return (i != end(variants))
? result->variant(i.value()) ? result->variant(i->second)
: result.get(); : result.get();
}(); }();
} }

View file

@ -535,6 +535,9 @@ void Application::badMtprotoConfigurationError() {
void Application::startLocalStorage() { void Application::startLocalStorage() {
Local::start(); Local::start();
_saveSettingsTimer.emplace([=] { saveSettings(); }); _saveSettingsTimer.emplace([=] { saveSettings(); });
_settings.saveDelayedRequests() | rpl::start_with_next([=] {
saveSettingsDelayed();
}, _lifetime);
} }
void Application::startEmojiImageLoader() { void Application::startEmojiImageLoader() {

View file

@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Core { namespace Core {
namespace { namespace {
constexpr auto kRecentEmojiLimit = 42;
[[nodiscard]] WindowPosition Deserialize(const QByteArray &data) { [[nodiscard]] WindowPosition Deserialize(const QByteArray &data) {
QDataStream stream(data); QDataStream stream(data);
stream.setVersion(QDataStream::Qt_5_1); stream.setVersion(QDataStream::Qt_5_1);
@ -75,6 +77,17 @@ QByteArray Settings::serialize() const {
const auto themesAccentColors = _themesAccentColors.serialize(); const auto themesAccentColors = _themesAccentColors.serialize();
const auto windowPosition = Serialize(_windowPosition); const auto windowPosition = Serialize(_windowPosition);
auto recentEmojiPreloadGenerated = std::vector<RecentEmojiId>();
if (_recentEmojiPreload.empty()) {
recentEmojiPreloadGenerated.reserve(_recentEmoji.size());
for (const auto [emoji, rating] : _recentEmoji) {
recentEmojiPreloadGenerated.push_back({ emoji->id(), rating });
}
}
const auto &recentEmojiPreloadData = _recentEmojiPreload.empty()
? recentEmojiPreloadGenerated
: _recentEmojiPreload;
auto size = Serialize::bytearraySize(themesAccentColors) auto size = Serialize::bytearraySize(themesAccentColors)
+ sizeof(qint32) * 5 + sizeof(qint32) * 5
+ Serialize::stringSize(_downloadPath.current()) + Serialize::stringSize(_downloadPath.current())
@ -83,10 +96,16 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_callOutputDeviceId) + Serialize::stringSize(_callOutputDeviceId)
+ Serialize::stringSize(_callInputDeviceId) + Serialize::stringSize(_callInputDeviceId)
+ Serialize::stringSize(_callVideoInputDeviceId) + Serialize::stringSize(_callVideoInputDeviceId)
+ sizeof(qint32) * 3; + sizeof(qint32) * 5;
for (const auto &[key, value] : _soundOverrides) { for (const auto &[key, value] : _soundOverrides) {
size += Serialize::stringSize(key) + Serialize::stringSize(value); size += Serialize::stringSize(key) + Serialize::stringSize(value);
} }
for (const auto &[id, rating] : recentEmojiPreloadData) {
size += Serialize::stringSize(id) + sizeof(quint16);
}
for (const auto &[id, variant] : _emojiVariants) {
size += Serialize::stringSize(id) + sizeof(quint8);
}
size += Serialize::bytearraySize(_videoPipGeometry); size += Serialize::bytearraySize(_videoPipGeometry);
size += Serialize::bytearraySize(windowPosition); size += Serialize::bytearraySize(windowPosition);
@ -165,7 +184,16 @@ QByteArray Settings::serialize() const {
<< qint64(_groupCallPushToTalkDelay) << qint64(_groupCallPushToTalkDelay)
<< qint32(0) // Call audio backend << qint32(0) // Call audio backend
<< qint32(_disableCalls ? 1 : 0) << qint32(_disableCalls ? 1 : 0)
<< windowPosition; << windowPosition
<< qint32(recentEmojiPreloadData.size());
for (const auto &[id, rating] : recentEmojiPreloadData) {
stream << id << quint16(rating);
}
stream
<< qint32(_emojiVariants.size());
for (const auto &[id, variant] : _emojiVariants) {
stream << id << quint8(variant);
}
} }
return result; return result;
} }
@ -239,6 +267,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 callAudioBackend = 0; qint32 callAudioBackend = 0;
qint32 disableCalls = _disableCalls ? 1 : 0; qint32 disableCalls = _disableCalls ? 1 : 0;
QByteArray windowPosition; QByteArray windowPosition;
std::vector<RecentEmojiId> recentEmojiPreload;
base::flat_map<QString, uint8> emojiVariants;
stream >> themesAccentColors; stream >> themesAccentColors;
if (!stream.atEnd()) { if (!stream.atEnd()) {
@ -343,6 +373,30 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) { if (!stream.atEnd()) {
stream >> windowPosition; stream >> windowPosition;
} }
if (!stream.atEnd()) {
auto recentCount = qint32(0);
stream >> recentCount;
if (recentCount > 0 && recentCount < 10000) {
recentEmojiPreload.reserve(recentCount);
for (auto i = 0; i != recentCount; ++i) {
auto id = QString();
auto rating = quint16();
stream >> id >> rating;
recentEmojiPreload.push_back({ id, rating });
}
}
auto variantsCount = qint32(0);
stream >> variantsCount;
if (variantsCount > 0 && variantsCount < 10000) {
emojiVariants.reserve(variantsCount);
for (auto i = 0; i != variantsCount; ++i) {
auto id = QString();
auto variant = quint8();
stream >> id >> variant;
emojiVariants.emplace(id, variant);
}
}
}
if (stream.status() != QDataStream::Ok) { if (stream.status() != QDataStream::Ok) {
LOG(("App Error: " LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()")); "Bad data for Core::Settings::constructFromSerialized()"));
@ -446,6 +500,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!windowPosition.isEmpty()) { if (!windowPosition.isEmpty()) {
_windowPosition = Deserialize(windowPosition); _windowPosition = Deserialize(windowPosition);
} }
_recentEmojiPreload = std::move(recentEmojiPreload);
_emojiVariants = std::move(emojiVariants);
} }
bool Settings::chatWide() const { bool Settings::chatWide() const {
@ -525,6 +581,118 @@ rpl::producer<int> Settings::thirdColumnWidthChanges() const {
return _thirdColumnWidth.changes(); return _thirdColumnWidth.changes();
} }
const std::vector<Settings::RecentEmoji> &Settings::recentEmoji() const {
if (_recentEmoji.empty()) {
resolveRecentEmoji();
}
return _recentEmoji;
}
void Settings::resolveRecentEmoji() const {
const auto haveAlready = [&](EmojiPtr emoji) {
return ranges::contains(
_recentEmoji,
emoji->id(),
[](const RecentEmoji &data) { return data.emoji->id(); });
};
if (!_recentEmojiPreload.empty()) {
_recentEmoji.reserve(_recentEmojiPreload.size());
for (const auto &[id, rating] : base::take(_recentEmojiPreload)) {
if (const auto emoji = Ui::Emoji::Find(id)) {
if (!haveAlready(emoji)) {
_recentEmoji.push_back({ emoji, rating });
}
}
}
_recentEmojiPreload.clear();
}
for (const auto emoji : Ui::Emoji::GetDefaultRecent()) {
if (_recentEmoji.size() >= kRecentEmojiLimit) {
break;
} else if (!haveAlready(emoji)) {
_recentEmoji.push_back({ emoji, 1 });
}
}
}
EmojiPack Settings::recentEmojiSection() const {
const auto &recent = recentEmoji();
auto result = EmojiPack();
result.reserve(recent.size());
for (const auto [emoji, rating] : recent) {
result.push_back(emoji);
}
return result;
}
void Settings::incrementRecentEmoji(EmojiPtr emoji) {
resolveRecentEmoji();
auto i = _recentEmoji.begin(), e = _recentEmoji.end();
for (; i != e; ++i) {
if (i->emoji == emoji) {
++i->rating;
if (i->rating > 0x8000) {
for (auto j = _recentEmoji.begin(); j != e; ++j) {
if (j->rating > 1) {
j->rating /= 2;
} else {
j->rating = 1;
}
}
}
for (; i != _recentEmoji.begin(); --i) {
if ((i - 1)->rating > i->rating) {
break;
}
std::swap(*i, *(i - 1));
}
break;
}
}
if (i == e) {
while (_recentEmoji.size() >= kRecentEmojiLimit) {
_recentEmoji.pop_back();
}
_recentEmoji.push_back({ emoji, 1 });
for (i = _recentEmoji.end() - 1; i != _recentEmoji.begin(); --i) {
if ((i - 1)->rating > i->rating) {
break;
}
std::swap(*i, *(i - 1));
}
}
_recentEmojiUpdated.fire({});
_saveDelayed.fire({});
}
void Settings::setLegacyRecentEmojiPreload(
QVector<QPair<QString, ushort>> data) {
if (!_recentEmojiPreload.empty() || data.isEmpty()) {
return;
}
_recentEmojiPreload.reserve(data.size());
for (const auto &[id, rating] : data) {
_recentEmojiPreload.push_back({ id, rating });
}
}
void Settings::saveEmojiVariant(EmojiPtr emoji) {
_emojiVariants[emoji->nonColoredId()] = emoji->variantIndex(emoji);
_saveDelayed.fire({});
}
void Settings::setLegacyEmojiVariants(QMap<QString, int> data) {
if (!_emojiVariants.empty() || data.isEmpty()) {
return;
}
_emojiVariants.reserve(data.size());
for (auto i = data.begin(), e = data.end(); i != e; ++i) {
_emojiVariants.emplace(i.key(), i.value());
}
}
void Settings::resetOnLastLogout() { void Settings::resetOnLastLogout() {
_adaptiveForWide = true; _adaptiveForWide = true;
_moderateModeEnabled = false; _moderateModeEnabled = false;
@ -592,6 +760,10 @@ void Settings::resetOnLastLogout() {
_notifyFromAll = true; _notifyFromAll = true;
_tabbedReplacedWithInfo = false; // per-window _tabbedReplacedWithInfo = false; // per-window
_systemDarkModeEnabled = false; _systemDarkModeEnabled = false;
_recentEmojiPreload.clear();
_recentEmoji.clear();
_emojiVariants.clear();
} }
bool Settings::ThirdColumnByDefault() { bool Settings::ThirdColumnByDefault() {

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_themes_embedded.h" #include "window/themes/window_themes_embedded.h"
#include "ui/chat/attach/attach_send_files_way.h" #include "ui/chat/attach/attach_send_files_way.h"
#include "platform/platform_notifications_manager.h" #include "platform/platform_notifications_manager.h"
#include "emoji.h"
enum class RectPart; enum class RectPart;
@ -52,6 +53,10 @@ public:
Settings(); Settings();
[[nodiscard]] rpl::producer<> saveDelayedRequests() const {
return _saveDelayed.events();
}
[[nodiscard]] static bool IsLeftCorner(ScreenCorner corner) { [[nodiscard]] static bool IsLeftCorner(ScreenCorner corner) {
return (corner == ScreenCorner::TopLeft) return (corner == ScreenCorner::TopLeft)
|| (corner == ScreenCorner::BottomLeft); || (corner == ScreenCorner::BottomLeft);
@ -509,8 +514,26 @@ public:
_windowPosition = position; _windowPosition = position;
} }
struct RecentEmoji {
EmojiPtr emoji = nullptr;
ushort rating = 0;
};
[[nodiscard]] const std::vector<RecentEmoji> &recentEmoji() const;
[[nodiscard]] EmojiPack recentEmojiSection() const;
void incrementRecentEmoji(EmojiPtr emoji);
void setLegacyRecentEmojiPreload(QVector<QPair<QString, ushort>> data);
[[nodiscard]] rpl::producer<> recentEmojiUpdated() const {
return _recentEmojiUpdated.events();
}
[[nodiscard]] const base::flat_map<QString, uint8> &emojiVariants() const {
return _emojiVariants;
}
void saveEmojiVariant(EmojiPtr emoji);
void setLegacyEmojiVariants(QMap<QString, int> data);
[[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] float64 DefaultDialogsWidthRatio(); [[nodiscard]] static float64 DefaultDialogsWidthRatio();
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
return int(std::round(std::clamp(speed, 0.5, 2.0) * 100)); return int(std::round(std::clamp(speed, 0.5, 2.0) * 100));
} }
@ -526,10 +549,17 @@ public:
void resetOnLastLogout(); void resetOnLastLogout();
private: private:
void resolveRecentEmoji() const;
static constexpr auto kDefaultThirdColumnWidth = 0; static constexpr auto kDefaultThirdColumnWidth = 0;
static constexpr auto kDefaultDialogsWidthRatio = 5. / 14; static constexpr auto kDefaultDialogsWidthRatio = 5. / 14;
static constexpr auto kDefaultBigDialogsWidthRatio = 0.275; static constexpr auto kDefaultBigDialogsWidthRatio = 0.275;
struct RecentEmojiId {
QString emoji;
ushort rating = 0;
};
bool _adaptiveForWide = true; bool _adaptiveForWide = true;
bool _moderateModeEnabled = false; bool _moderateModeEnabled = false;
rpl::variable<float64> _songVolume = kDefaultVolume; rpl::variable<float64> _songVolume = kDefaultVolume;
@ -577,6 +607,10 @@ private:
rpl::variable<std::vector<int>> _dictionariesEnabled; rpl::variable<std::vector<int>> _dictionariesEnabled;
rpl::variable<bool> _autoDownloadDictionaries = true; rpl::variable<bool> _autoDownloadDictionaries = true;
rpl::variable<bool> _mainMenuAccountsShown = true; rpl::variable<bool> _mainMenuAccountsShown = true;
mutable std::vector<RecentEmojiId> _recentEmojiPreload;
mutable std::vector<RecentEmoji> _recentEmoji;
base::flat_map<QString, uint8> _emojiVariants;
rpl::event_stream<> _recentEmojiUpdated;
bool _tabbedSelectorSectionEnabled = false; // per-window bool _tabbedSelectorSectionEnabled = false; // per-window
Window::Column _floatPlayerColumn = Window::Column(); // per-window Window::Column _floatPlayerColumn = Window::Column(); // per-window
RectPart _floatPlayerCorner = RectPart(); // per-window RectPart _floatPlayerCorner = RectPart(); // per-window
@ -594,6 +628,7 @@ private:
bool _tabbedReplacedWithInfo = false; // per-window bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
rpl::event_stream<> _saveDelayed;
float64 _rememberedSongVolume = kDefaultVolume; float64 _rememberedSongVolume = kDefaultVolume;
bool _rememberedSoundNotifyFromTray = false; bool _rememberedSoundNotifyFromTray = false;
bool _rememberedFlashBounceNotifyFromTray = false; bool _rememberedFlashBounceNotifyFromTray = false;

View file

@ -234,11 +234,12 @@ const Ui::Emoji::One *UiIntegration::defaultEmojiVariant(
return emoji; return emoji;
} }
const auto nonColored = emoji->nonColoredId(); const auto nonColored = emoji->nonColoredId();
const auto it = cEmojiVariants().constFind(nonColored); const auto &variants = Core::App().settings().emojiVariants();
const auto result = (it != cEmojiVariants().cend()) const auto i = variants.find(nonColored);
? emoji->variant(it.value()) const auto result = (i != end(variants))
? emoji->variant(i->second)
: emoji; : emoji;
AddRecentEmoji(result); Core::App().settings().incrementRecentEmoji(result);
return result; return result;
} }

View file

@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "chat_helpers/emoji_list_widget.h" #include "chat_helpers/emoji_list_widget.h"
#include "core/sandbox.h" #include "core/sandbox.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
@ -265,7 +267,7 @@ void AppendEmojiPacks(
for (auto i = 0; i != ChatHelpers::kEmojiSectionCount; ++i) { for (auto i = 0; i != ChatHelpers::kEmojiSectionCount; ++i) {
const auto section = static_cast<Ui::Emoji::Section>(i); const auto section = static_cast<Ui::Emoji::Section>(i);
const auto list = (section == Ui::Emoji::Section::Recent) const auto list = (section == Ui::Emoji::Section::Recent)
? GetRecentEmojiSection() ? Core::App().settings().recentEmojiSection()
: Ui::Emoji::GetSection(section); : Ui::Emoji::GetSection(section);
const auto title = (section == Ui::Emoji::Section::Recent) const auto title = (section == Ui::Emoji::Section::Recent)
? TitleRecentlyUsed(sets) ? TitleRecentlyUsed(sets)
@ -473,7 +475,7 @@ void AppendEmojiPacks(
if (const auto inputField = qobject_cast<QTextEdit*>( if (const auto inputField = qobject_cast<QTextEdit*>(
QApplication::focusWidget())) { QApplication::focusWidget())) {
Ui::InsertEmojiAtCursor(inputField->textCursor(), emoji); Ui::InsertEmojiAtCursor(inputField->textCursor(), emoji);
AddRecentEmoji(emoji); Core::App().settings().incrementRecentEmoji(emoji);
return true; return true;
} }
} }
@ -567,7 +569,7 @@ void AppendEmojiPacks(
_session->data().stickers().recentUpdated() _session->data().stickers().recentUpdated()
) | rpl::map_to(ScrubberItemType::Sticker), ) | rpl::map_to(ScrubberItemType::Sticker),
rpl::merge( rpl::merge(
UpdatedRecentEmoji(), Core::App().settings().recentEmojiUpdated(),
Ui::Emoji::Updated() Ui::Emoji::Updated()
) | rpl::map_to(ScrubberItemType::Emoji) ) | rpl::map_to(ScrubberItemType::Emoji)
) | rpl::start_with_next([=](ScrubberItemType type) { ) | rpl::start_with_next([=](ScrubberItemType type) {

View file

@ -9,14 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
namespace {
constexpr auto kRecentEmojiLimit = 42;
auto UpdatesRecentEmoji = rpl::event_stream<>();
} // namespace
Qt::LayoutDirection gLangDir = Qt::LeftToRight; Qt::LayoutDirection gLangDir = Qt::LeftToRight;
bool gInstallBetaVersion = AppBetaVersion; bool gInstallBetaVersion = AppBetaVersion;
@ -56,10 +48,6 @@ int gConfigScale = style::kScaleAuto;
QString gTimeFormat = qsl("hh:mm"); QString gTimeFormat = qsl("hh:mm");
RecentEmojiPack gRecentEmoji;
RecentEmojiPreload gRecentEmojiPreload;
EmojiColorVariants gEmojiVariants;
RecentStickerPreload gRecentStickersPreload; RecentStickerPreload gRecentStickersPreload;
RecentStickerPack gRecentStickers; RecentStickerPack gRecentStickers;
@ -79,92 +67,3 @@ int gOtherOnline = 0;
int32 gAutoDownloadPhoto = 0; // all auto download int32 gAutoDownloadPhoto = 0; // all auto download
int32 gAutoDownloadAudio = 0; int32 gAutoDownloadAudio = 0;
int32 gAutoDownloadGif = 0; int32 gAutoDownloadGif = 0;
RecentEmojiPack &GetRecentEmoji() {
if (cRecentEmoji().isEmpty()) {
RecentEmojiPack result;
auto haveAlready = [&result](EmojiPtr emoji) {
for (auto &row : result) {
if (row.first->id() == emoji->id()) {
return true;
}
}
return false;
};
if (!cRecentEmojiPreload().isEmpty()) {
auto preload = cRecentEmojiPreload();
cSetRecentEmojiPreload(RecentEmojiPreload());
result.reserve(preload.size());
for (auto i = preload.cbegin(), e = preload.cend(); i != e; ++i) {
if (auto emoji = Ui::Emoji::Find(i->first)) {
if (!haveAlready(emoji)) {
result.push_back(qMakePair(emoji, i->second));
}
}
}
}
for (const auto emoji : Ui::Emoji::GetDefaultRecent()) {
if (result.size() >= kRecentEmojiLimit) break;
if (!haveAlready(emoji)) {
result.push_back(qMakePair(emoji, 1));
}
}
cSetRecentEmoji(result);
}
return cRefRecentEmoji();
}
EmojiPack GetRecentEmojiSection() {
const auto &recent = GetRecentEmoji();
auto result = EmojiPack();
result.reserve(recent.size());
for (const auto &item : recent) {
result.push_back(item.first);
}
return result;
}
void AddRecentEmoji(EmojiPtr emoji) {
auto &recent = GetRecentEmoji();
auto i = recent.begin(), e = recent.end();
for (; i != e; ++i) {
if (i->first == emoji) {
++i->second;
if (i->second > 0x8000) {
for (auto j = recent.begin(); j != e; ++j) {
if (j->second > 1) {
j->second /= 2;
} else {
j->second = 1;
}
}
}
for (; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
std::swap(*i, *(i - 1));
}
break;
}
}
if (i == e) {
while (recent.size() >= kRecentEmojiLimit) {
recent.pop_back();
}
recent.push_back(qMakePair(emoji, 1));
for (i = recent.end() - 1; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
std::swap(*i, *(i - 1));
}
}
UpdatesRecentEmoji.fire({});
}
rpl::producer<> UpdatedRecentEmoji() {
return UpdatesRecentEmoji.events();
}

View file

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/style/style_core.h" #include "ui/style/style_core.h"
#include "emoji.h"
#define DeclareReadSetting(Type, Name) extern Type g##Name; \ #define DeclareReadSetting(Type, Name) extern Type g##Name; \
inline const Type &c##Name() { \ inline const Type &c##Name() { \
@ -84,16 +83,6 @@ DeclareSetting(int, ScreenScale);
DeclareSetting(int, ConfigScale); DeclareSetting(int, ConfigScale);
DeclareSetting(QString, TimeFormat); DeclareSetting(QString, TimeFormat);
using RecentEmojiPreloadOldOld = QVector<QPair<uint32, ushort>>;
using RecentEmojiPreloadOld = QVector<QPair<uint64, ushort>>;
using RecentEmojiPreload = QVector<QPair<QString, ushort>>;
using RecentEmojiPack = QVector<QPair<EmojiPtr, ushort>>;
using EmojiColorVariantsOld = QMap<uint32, uint64>;
using EmojiColorVariants = QMap<QString, int>;
DeclareRefSetting(RecentEmojiPack, RecentEmoji);
DeclareSetting(RecentEmojiPreload, RecentEmojiPreload);
DeclareRefSetting(EmojiColorVariants, EmojiVariants);
class DocumentData; class DocumentData;
typedef QList<QPair<DocumentData*, int16>> RecentStickerPackOld; typedef QList<QPair<DocumentData*, int16>> RecentStickerPackOld;
@ -124,11 +113,6 @@ inline void cChangeTimeFormat(const QString &newFormat) {
if (!newFormat.isEmpty()) cSetTimeFormat(newFormat); if (!newFormat.isEmpty()) cSetTimeFormat(newFormat);
} }
RecentEmojiPack &GetRecentEmoji();
QVector<EmojiPtr> GetRecentEmojiSection();
void AddRecentEmoji(EmojiPtr emoji);
[[nodiscard]] rpl::producer<> UpdatedRecentEmoji();
inline bool passcodeCanTry() { inline bool passcodeCanTry() {
if (cPasscodeBadTries() < 3) return true; if (cPasscodeBadTries() < 3) return true;
auto dt = crl::now() - cPasscodeLastTry(); auto dt = crl::now() - cPasscodeLastTry();

View file

@ -944,13 +944,13 @@ bool ReadSetting(
context.legacyRead = true; context.legacyRead = true;
} break; } break;
case dbiRecentEmojiOldOld: { case dbiRecentEmojiOldOldOld: {
RecentEmojiPreloadOldOld v; auto v = QVector<QPair<uint32, ushort>>();
stream >> v; stream >> v;
if (!CheckStreamStatus(stream)) return false; if (!CheckStreamStatus(stream)) return false;
if (!v.isEmpty()) { if (!v.isEmpty()) {
RecentEmojiPreload p; auto p = QVector<QPair<QString, ushort>>();
p.reserve(v.size()); p.reserve(v.size());
for (auto &item : v) { for (auto &item : v) {
auto oldKey = uint64(item.first); auto oldKey = uint64(item.first);
@ -971,18 +971,18 @@ bool ReadSetting(
p.push_back(qMakePair(id, item.second)); p.push_back(qMakePair(id, item.second));
} }
} }
cSetRecentEmojiPreload(p); Core::App().settings().setLegacyRecentEmojiPreload(std::move(p));
} }
context.legacyRead = true; context.legacyRead = true;
} break; } break;
case dbiRecentEmojiOld: { case dbiRecentEmojiOldOld: {
RecentEmojiPreloadOld v; auto v = QVector<QPair<uint64, ushort>>();
stream >> v; stream >> v;
if (!CheckStreamStatus(stream)) return false; if (!CheckStreamStatus(stream)) return false;
if (!v.isEmpty()) { if (!v.isEmpty()) {
RecentEmojiPreload p; auto p = QVector<QPair<QString, ushort>>();
p.reserve(v.size()); p.reserve(v.size());
for (auto &item : v) { for (auto &item : v) {
auto id = Ui::Emoji::IdFromOldKey(item.first); auto id = Ui::Emoji::IdFromOldKey(item.first);
@ -990,17 +990,18 @@ bool ReadSetting(
p.push_back(qMakePair(id, item.second)); p.push_back(qMakePair(id, item.second));
} }
} }
cSetRecentEmojiPreload(p); Core::App().settings().setLegacyRecentEmojiPreload(std::move(p));
} }
context.legacyRead = true; context.legacyRead = true;
} break; } break;
case dbiRecentEmoji: { case dbiRecentEmojiOld: {
RecentEmojiPreload v; auto v = QVector<QPair<QString, ushort>>();
stream >> v; stream >> v;
if (!CheckStreamStatus(stream)) return false; if (!CheckStreamStatus(stream)) return false;
cSetRecentEmojiPreload(v); Core::App().settings().setLegacyRecentEmojiPreload(std::move(v));
context.legacyRead = true;
} break; } break;
case dbiRecentStickers: { case dbiRecentStickers: {
@ -1011,12 +1012,12 @@ bool ReadSetting(
cSetRecentStickersPreload(v); cSetRecentStickersPreload(v);
} break; } break;
case dbiEmojiVariantsOld: { case dbiEmojiVariantsOldOld: {
EmojiColorVariantsOld v; auto v = QMap<uint32, uint64>();
stream >> v; stream >> v;
if (!CheckStreamStatus(stream)) return false; if (!CheckStreamStatus(stream)) return false;
EmojiColorVariants variants; auto variants = QMap<QString, int>();
for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) { for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) {
auto id = Ui::Emoji::IdFromOldKey(static_cast<uint64>(i.key())); auto id = Ui::Emoji::IdFromOldKey(static_cast<uint64>(i.key()));
if (!id.isEmpty()) { if (!id.isEmpty()) {
@ -1026,16 +1027,17 @@ bool ReadSetting(
} }
} }
} }
cSetEmojiVariants(variants); Core::App().settings().setLegacyEmojiVariants(std::move(variants));
context.legacyRead = true; context.legacyRead = true;
} break; } break;
case dbiEmojiVariants: { case dbiEmojiVariantsOld: {
EmojiColorVariants v; auto v = QMap<QString, int>();
stream >> v; stream >> v;
if (!CheckStreamStatus(stream)) return false; if (!CheckStreamStatus(stream)) return false;
cSetEmojiVariants(v); Core::App().settings().setLegacyEmojiVariants(std::move(v));
context.legacyRead = true;
} break; } break;
case dbiHiddenPinnedMessagesOld: { case dbiHiddenPinnedMessagesOld: {

View file

@ -101,7 +101,7 @@ enum {
dbiDownloadPathOldOld = 0x15, dbiDownloadPathOldOld = 0x15,
dbiScaleOld = 0x16, dbiScaleOld = 0x16,
dbiEmojiTabOld = 0x17, dbiEmojiTabOld = 0x17,
dbiRecentEmojiOldOld = 0x18, dbiRecentEmojiOldOldOld = 0x18,
dbiLoggedPhoneNumberOld = 0x19, dbiLoggedPhoneNumberOld = 0x19,
dbiMutedPeersOld = 0x1a, dbiMutedPeersOld = 0x1a,
// 0x1b reserved // 0x1b reserved
@ -113,8 +113,8 @@ enum {
dbiTileBackgroundOld = 0x21, dbiTileBackgroundOld = 0x21,
dbiAutoLockOld = 0x22, dbiAutoLockOld = 0x22,
dbiDialogLastPath = 0x23, dbiDialogLastPath = 0x23,
dbiRecentEmojiOld = 0x24, dbiRecentEmojiOldOld = 0x24,
dbiEmojiVariantsOld = 0x25, dbiEmojiVariantsOldOld = 0x25,
dbiRecentStickers = 0x26, dbiRecentStickers = 0x26,
dbiDcOptionOld = 0x27, dbiDcOptionOld = 0x27,
dbiTryIPv6 = 0x28, dbiTryIPv6 = 0x28,
@ -129,8 +129,8 @@ enum {
dbiAutoPlayOld = 0x37, dbiAutoPlayOld = 0x37,
dbiAdaptiveForWideOld = 0x38, dbiAdaptiveForWideOld = 0x38,
dbiHiddenPinnedMessagesOld = 0x39, dbiHiddenPinnedMessagesOld = 0x39,
dbiRecentEmoji = 0x3a, dbiRecentEmojiOld = 0x3a,
dbiEmojiVariants = 0x3b, dbiEmojiVariantsOld = 0x3b,
dbiDialogsModeOld = 0x40, dbiDialogsModeOld = 0x40,
dbiModerateModeOld = 0x41, dbiModerateModeOld = 0x41,
dbiVideoVolumeOld = 0x42, dbiVideoVolumeOld = 0x42,

View file

@ -734,13 +734,6 @@ void Account::writeSessionSettings(Main::SessionSettings *stored) {
writeMapQueued(); writeMapQueued();
} }
auto recentEmojiPreloadData = cRecentEmojiPreload();
if (recentEmojiPreloadData.isEmpty()) {
recentEmojiPreloadData.reserve(GetRecentEmoji().size());
for (auto &item : GetRecentEmoji()) {
recentEmojiPreloadData.push_back(qMakePair(item.first->id(), item.second));
}
}
auto userDataInstance = stored auto userDataInstance = stored
? stored ? stored
: _owner->getSessionSettings(); : _owner->getSessionSettings();
@ -759,13 +752,6 @@ void Account::writeSessionSettings(Main::SessionSettings *stored) {
uint32 size = 24 * (sizeof(quint32) + sizeof(qint32)); uint32 size = 24 * (sizeof(quint32) + sizeof(qint32));
size += sizeof(quint32); size += sizeof(quint32);
size += sizeof(quint32) + sizeof(qint32);
for (auto &item : recentEmojiPreloadData) {
size += Serialize::stringSize(item.first) + sizeof(item.second);
}
size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64));
size += sizeof(quint32) + sizeof(qint32) + recentStickers.size() * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + sizeof(qint32) + recentStickers.size() * (sizeof(uint64) + sizeof(ushort));
size += sizeof(quint32) + 3 * sizeof(qint32); size += sizeof(quint32) + 3 * sizeof(qint32);
size += sizeof(quint32) + 2 * sizeof(qint32); size += sizeof(quint32) + 2 * sizeof(qint32);
@ -780,8 +766,6 @@ void Account::writeSessionSettings(Main::SessionSettings *stored) {
if (!userData.isEmpty()) { if (!userData.isEmpty()) {
data.stream << quint32(dbiSessionSettings) << userData; data.stream << quint32(dbiSessionSettings) << userData;
} }
data.stream << quint32(dbiRecentEmoji) << recentEmojiPreloadData;
data.stream << quint32(dbiEmojiVariants) << cEmojiVariants();
data.stream << quint32(dbiRecentStickers) << recentStickers; data.stream << quint32(dbiRecentStickers) << recentStickers;
FileWriteDescriptor file(_settingsKey, _basePath); FileWriteDescriptor file(_settingsKey, _basePath);