feat: new message history viewer initial

fix: use Qt formatting

kanged from `admin_log` and cleaned up a bit
This commit is contained in:
ZavaruKitsu 2023-09-14 17:36:14 +03:00
parent ba93a50062
commit e1babcf69a
56 changed files with 6953 additions and 2561 deletions

View file

@ -102,6 +102,8 @@ PRIVATE
ayu/ayu_lang.cpp ayu/ayu_lang.cpp
ayu/ayu_lang.h ayu/ayu_lang.h
ayu/ayu_constants.h ayu/ayu_constants.h
ayu/utils/ayu_mapper.cpp
ayu/utils/ayu_mapper.h
ayu/utils/telegram_helpers.cpp ayu/utils/telegram_helpers.cpp
ayu/utils/telegram_helpers.h ayu/utils/telegram_helpers.h
ayu/utils/windows_utils.cpp ayu/utils/windows_utils.cpp
@ -118,6 +120,12 @@ PRIVATE
ayu/ui/settings/settings_ayu.h ayu/ui/settings/settings_ayu.h
ayu/ui/context_menu/context_menu.cpp ayu/ui/context_menu/context_menu.cpp
ayu/ui/context_menu/context_menu.h ayu/ui/context_menu/context_menu.h
ayu/ui/sections/edited/edited_log_inner.cpp
ayu/ui/sections/edited/edited_log_inner.h
ayu/ui/sections/edited/edited_log_item.cpp
ayu/ui/sections/edited/edited_log_item.h
ayu/ui/sections/edited/edited_log_section.cpp
ayu/ui/sections/edited/edited_log_section.h
ayu/ui/boxes/voice_confirmation_box.cpp ayu/ui/boxes/voice_confirmation_box.cpp
ayu/ui/boxes/voice_confirmation_box.h ayu/ui/boxes/voice_confirmation_box.h
ayu/ui/boxes/message_history_box.cpp ayu/ui/boxes/message_history_box.cpp

View file

@ -4,11 +4,13 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
// https://github.com/AyuGram/AyuGram4A/blob/rewrite/TMessagesProj/src/main/java/com/radolyn/ayugram/AyuConstants.java // https://github.com/AyuGram/AyuGram4AX/blob/rewrite/TMessagesProj/src/main/java/com/radolyn/ayugram/AyuConstants.java
constexpr int DOCUMENT_TYPE_NONE = 0; constexpr int DOCUMENT_TYPE_NONE = 0;
constexpr int DOCUMENT_TYPE_PHOTO = 1; constexpr int DOCUMENT_TYPE_PHOTO = 1;
constexpr int DOCUMENT_TYPE_STICKER = 2; constexpr int DOCUMENT_TYPE_STICKER = 2;
constexpr int DOCUMENT_TYPE_FILE = 3; constexpr int DOCUMENT_TYPE_FILE = 3;

View file

@ -32,8 +32,7 @@ void CustomLangPack::fetchCustomLangPack(const QString& langPackId, const QStrin
LOG(("Current Language pack ID: %1, Base ID: %2").arg(langPackId, langPackBaseId)); LOG(("Current Language pack ID: %1, Base ID: %2").arg(langPackId, langPackBaseId));
auto finalLangPackId = langPackId; auto finalLangPackId = langPackId;
if (finalLangPackId == qsl("pt-br")) if (finalLangPackId == qsl("pt-br")) {
{
// meh // meh
finalLangPackId = qsl("pt"); finalLangPackId = qsl("pt");
} }
@ -41,20 +40,17 @@ void CustomLangPack::fetchCustomLangPack(const QString& langPackId, const QStrin
const auto proxy = Core::App().settings().proxy().isEnabled() const auto proxy = Core::App().settings().proxy().isEnabled()
? Core::App().settings().proxy().selected() ? Core::App().settings().proxy().selected()
: MTP::ProxyData(); : MTP::ProxyData();
if (proxy.type == MTP::ProxyData::Type::Socks5 || proxy.type == MTP::ProxyData::Type::Http) if (proxy.type == MTP::ProxyData::Type::Socks5 || proxy.type == MTP::ProxyData::Type::Http) {
{
QNetworkProxy LocaleProxy = ToNetworkProxy(ToDirectIpProxy(proxy)); QNetworkProxy LocaleProxy = ToNetworkProxy(ToDirectIpProxy(proxy));
networkManager.setProxy(LocaleProxy); networkManager.setProxy(LocaleProxy);
} }
QUrl url; QUrl url;
if (!finalLangPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) if (!finalLangPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
{
url.setUrl(qsl("https://raw.githubusercontent.com/AyuGram/Languages/l10n_main/values/langs/%1/Shared.json").arg( url.setUrl(qsl("https://raw.githubusercontent.com/AyuGram/Languages/l10n_main/values/langs/%1/Shared.json").arg(
finalLangPackId)); finalLangPackId));
} }
else else {
{
url.setUrl(qsl("https://raw.githubusercontent.com/AyuGram/Languages/l10n_main/values/langs/%1/Shared.json").arg( url.setUrl(qsl("https://raw.githubusercontent.com/AyuGram/Languages/l10n_main/values/langs/%1/Shared.json").arg(
needFallback ? langPackBaseId : finalLangPackId)); needFallback ? langPackBaseId : finalLangPackId));
} }
@ -73,24 +69,20 @@ void CustomLangPack::fetchFinished()
QString langPackId = Lang::GetInstance().id(); QString langPackId = Lang::GetInstance().id();
auto statusCode = _chkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); auto statusCode = _chkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 404 && !langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) if (statusCode == 404 && !langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
{
LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId)); LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId));
needFallback = true; needFallback = true;
_chkReply->disconnect(); _chkReply->disconnect();
fetchCustomLangPack("", langPackBaseId); fetchCustomLangPack("", langPackBaseId);
} }
else else {
{
QByteArray result = _chkReply->readAll().trimmed(); QByteArray result = _chkReply->readAll().trimmed();
QJsonParseError error{}; QJsonParseError error{};
QJsonDocument str = QJsonDocument::fromJson(result, &error); QJsonDocument str = QJsonDocument::fromJson(result, &error);
if (error.error == QJsonParseError::NoError) if (error.error == QJsonParseError::NoError) {
{
parseLangFile(str); parseLangFile(str);
} }
else else {
{
LOG(("Incorrect JSON File. Fallback to default language: English...")); LOG(("Incorrect JSON File. Fallback to default language: English..."));
loadDefaultLangFile(); loadDefaultLangFile();
} }
@ -103,20 +95,17 @@ void CustomLangPack::fetchError(QNetworkReply::NetworkError e)
{ {
LOG(("Network error: %1").arg(e)); LOG(("Network error: %1").arg(e));
if (e == QNetworkReply::NetworkError::ContentNotFoundError) if (e == QNetworkReply::NetworkError::ContentNotFoundError) {
{
QString langPackBaseId = Lang::GetInstance().baseId(); QString langPackBaseId = Lang::GetInstance().baseId();
QString langPackId = Lang::GetInstance().id(); QString langPackId = Lang::GetInstance().id();
if (!langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) if (!langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
{
LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId)); LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId));
needFallback = true; needFallback = true;
_chkReply->disconnect(); _chkReply->disconnect();
fetchCustomLangPack("", langPackBaseId); fetchCustomLangPack("", langPackBaseId);
} }
else else {
{
LOG(("AyuGram Language pack not found! Fallback to default language: English...")); LOG(("AyuGram Language pack not found! Fallback to default language: English..."));
loadDefaultLangFile(); loadDefaultLangFile();
_chkReply = nullptr; _chkReply = nullptr;
@ -127,12 +116,10 @@ void CustomLangPack::fetchError(QNetworkReply::NetworkError e)
void CustomLangPack::loadDefaultLangFile() void CustomLangPack::loadDefaultLangFile()
{ {
QFile file(":/localization/en.json"); QFile file(":/localization/en.json");
if (file.open(QIODevice::ReadOnly)) if (file.open(QIODevice::ReadOnly)) {
{
QJsonDocument str = QJsonDocument::fromJson(file.readAll()); QJsonDocument str = QJsonDocument::fromJson(file.readAll());
QJsonObject json = str.object(); QJsonObject json = str.object();
for (const QString& key : json.keys()) for (const QString &key : json.keys()) {
{
Lang::GetInstance().applyValue(key.toUtf8(), json.value(key).toString().toUtf8()); Lang::GetInstance().applyValue(key.toUtf8(), json.value(key).toString().toUtf8());
} }
Lang::GetInstance().updatePluralRules(); Lang::GetInstance().updatePluralRules();
@ -143,15 +130,13 @@ void CustomLangPack::loadDefaultLangFile()
void CustomLangPack::parseLangFile(QJsonDocument str) void CustomLangPack::parseLangFile(QJsonDocument str)
{ {
QJsonObject json = str.object(); QJsonObject json = str.object();
for (const QString& brokenKey : json.keys()) for (const QString &brokenKey : json.keys()) {
{
auto key = qsl("ayu_") + brokenKey; auto key = qsl("ayu_") + brokenKey;
auto val = json.value(brokenKey).toString().replace(qsl("&"), qsl("&")).toUtf8(); auto val = json.value(brokenKey).toString().replace(qsl("&"), qsl("&")).toUtf8();
Lang::GetInstance().resetValue(key.toUtf8()); Lang::GetInstance().resetValue(key.toUtf8());
Lang::GetInstance().applyValue(key.toUtf8(), val); Lang::GetInstance().applyValue(key.toUtf8(), val);
if (key.contains("#other")) if (key.contains("#other")) {
{
Lang::GetInstance().resetValue(key.toUtf8().replace("#other", "#few")); Lang::GetInstance().resetValue(key.toUtf8().replace("#other", "#few"));
Lang::GetInstance().resetValue(key.toUtf8().replace("#other", "#few")); Lang::GetInstance().resetValue(key.toUtf8().replace("#other", "#few"));
Lang::GetInstance().applyValue(key.toUtf8().replace("#other", "#few"), val); Lang::GetInstance().applyValue(key.toUtf8().replace("#other", "#few"), val);

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>

View file

@ -14,17 +14,25 @@ using json = nlohmann::json;
namespace AyuSettings namespace AyuSettings
{ {
const QString filename = "tdata/ayu_settings.json"; const QString filename = "tdata/ayu_settings.json";
std::optional<AyuGramSettings> settings = std::nullopt; std::optional<AyuGramSettings> settings = std::nullopt;
rpl::variable<bool> sendReadMessagesReactive; rpl::variable<bool> sendReadMessagesReactive;
rpl::variable<bool> sendReadStoriesReactive; rpl::variable<bool> sendReadStoriesReactive;
rpl::variable<bool> sendOnlinePacketsReactive; rpl::variable<bool> sendOnlinePacketsReactive;
rpl::variable<bool> sendUploadProgressReactive; rpl::variable<bool> sendUploadProgressReactive;
rpl::variable<bool> sendOfflinePacketAfterOnlineReactive; rpl::variable<bool> sendOfflinePacketAfterOnlineReactive;
rpl::variable<QString> deletedMarkReactive; rpl::variable<QString> deletedMarkReactive;
rpl::variable<QString> editedMarkReactive; rpl::variable<QString> editedMarkReactive;
rpl::variable<int> showPeerIdReactive; rpl::variable<int> showPeerIdReactive;
rpl::variable<bool> ghostModeEnabled; rpl::variable<bool> ghostModeEnabled;
@ -43,8 +51,7 @@ namespace AyuSettings
void initialize() void initialize()
{ {
if (settings.has_value()) if (settings.has_value()) {
{
return; return;
} }
@ -55,7 +62,8 @@ namespace AyuSettings
return (val != settings->sendReadMessages); return (val != settings->sendReadMessages);
}) | start_with_next([=](bool val) }) | start_with_next([=](bool val)
{ {
ghostModeEnabled = ghostModeEnabled_util(settings.value()); ghostModeEnabled =
ghostModeEnabled_util(settings.value());
}, lifetime); }, lifetime);
// .. // ..
sendReadStoriesReactive.value() | rpl::filter([=](bool val) sendReadStoriesReactive.value() | rpl::filter([=](bool val)
@ -63,7 +71,8 @@ namespace AyuSettings
return (val != settings->sendReadStories); return (val != settings->sendReadStories);
}) | start_with_next([=](bool val) }) | start_with_next([=](bool val)
{ {
ghostModeEnabled = ghostModeEnabled_util(settings.value()); ghostModeEnabled =
ghostModeEnabled_util(settings.value());
}, lifetime); }, lifetime);
// .. // ..
sendOnlinePacketsReactive.value() | rpl::filter([=](bool val) sendOnlinePacketsReactive.value() | rpl::filter([=](bool val)
@ -71,7 +80,9 @@ namespace AyuSettings
return (val != settings->sendOnlinePackets); return (val != settings->sendOnlinePackets);
}) | start_with_next([=](bool val) }) | start_with_next([=](bool val)
{ {
ghostModeEnabled = ghostModeEnabled_util(settings.value()); ghostModeEnabled =
ghostModeEnabled_util(settings
.value());
}, lifetime); }, lifetime);
// .. // ..
sendUploadProgressReactive.value() | rpl::filter([=](bool val) sendUploadProgressReactive.value() | rpl::filter([=](bool val)
@ -79,15 +90,20 @@ namespace AyuSettings
return (val != settings->sendUploadProgress); return (val != settings->sendUploadProgress);
}) | start_with_next([=](bool val) }) | start_with_next([=](bool val)
{ {
ghostModeEnabled = ghostModeEnabled_util(settings.value()); ghostModeEnabled =
ghostModeEnabled_util(settings
.value());
}, lifetime); }, lifetime);
// .. // ..
sendOfflinePacketAfterOnlineReactive.value() | rpl::filter([=](bool val) sendOfflinePacketAfterOnlineReactive.value() | rpl::filter([=](bool val)
{ {
return (val != settings->sendOfflinePacketAfterOnline); return (val
!= settings->sendOfflinePacketAfterOnline);
}) | start_with_next([=](bool val) }) | start_with_next([=](bool val)
{ {
ghostModeEnabled = ghostModeEnabled_util(settings.value()); ghostModeEnabled =
ghostModeEnabled_util(
settings.value());
}, lifetime); }, lifetime);
} }
@ -115,8 +131,7 @@ namespace AyuSettings
void load() void load()
{ {
QFile file(filename); QFile file(filename);
if (!file.exists()) if (!file.exists()) {
{
return; return;
} }
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
@ -125,12 +140,10 @@ namespace AyuSettings
initialize(); initialize();
json p = json::parse(data); json p = json::parse(data);
try try {
{
settings = p.get<AyuGramSettings>(); settings = p.get<AyuGramSettings>();
} }
catch (...) catch (...) {
{
LOG(("AyuGramSettings: failed to parse settings file")); LOG(("AyuGramSettings: failed to parse settings file"));
} }
postinitialize(); postinitialize();
@ -311,4 +324,5 @@ namespace AyuSettings
{ {
return ghostModeEnabled.value(); return ghostModeEnabled.value();
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "lang_auto.h" #include "lang_auto.h"
@ -14,8 +13,11 @@
namespace AyuSettings namespace AyuSettings
{ {
const auto DEFAULT_ICON = QString("default"); const auto DEFAULT_ICON = QString("default");
const auto ALT_ICON = QString("alt"); const auto ALT_ICON = QString("alt");
const auto NOTHING_ICON = QString("nothing"); const auto NOTHING_ICON = QString("nothing");
class AyuGramSettings class AyuGramSettings
@ -186,4 +188,5 @@ namespace AyuSettings
bool get_ghostModeEnabled(); bool get_ghostModeEnabled();
rpl::producer<bool> get_ghostModeEnabledReactive(); rpl::producer<bool> get_ghostModeEnabledReactive();
} }

View file

@ -9,6 +9,7 @@
namespace AyuState namespace AyuState
{ {
void setAllowSendReadPacket(bool val, int resetAfter) void setAllowSendReadPacket(bool val, int resetAfter)
{ {
allowSendReadPacket.val = val; allowSendReadPacket.val = val;
@ -20,4 +21,5 @@ namespace AyuState
auto settings = &AyuSettings::getInstance(); auto settings = &AyuSettings::getInstance();
return settings->sendReadMessages || processVariable(allowSendReadPacket); return settings->sendReadMessages || processVariable(allowSendReadPacket);
} }
} }

View file

@ -4,15 +4,16 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ayu_settings.h" #include "ayu_settings.h"
namespace AyuState namespace AyuState
{ {
namespace namespace
{ {
class AyuStateVariable class AyuStateVariable
{ {
public: public:
@ -24,24 +25,24 @@ namespace AyuState
bool processVariable(AyuStateVariable &variable) bool processVariable(AyuStateVariable &variable)
{ {
if (variable.resetAfter == -1) if (variable.resetAfter == -1) {
{
return variable.val; return variable.val;
} }
variable.resetAfter -= 1; variable.resetAfter -= 1;
auto val = variable.val; auto val = variable.val;
if (variable.resetAfter == 0) if (variable.resetAfter == 0) {
{
variable.val = false; variable.val = false;
} }
return val; return val;
} }
} }
void setAllowSendReadPacket(bool val, int resetAfter = 1); void setAllowSendReadPacket(bool val, int resetAfter = 1);
bool getAllowSendPacket(); bool getAllowSendPacket();
} }

View file

@ -9,68 +9,92 @@
#include "entities.h" #include "entities.h"
#include "ayu/libs/sqlite/sqlite_orm.h" #include "ayu/libs/sqlite/sqlite_orm.h"
#include "ayu/utils/telegram_helpers.h"
using namespace sqlite_orm; using namespace sqlite_orm;
auto storage = make_storage(
"ayugram.db", auto storage = make_storage("ayugram.db",
make_table( make_table("DeletedMessage",
"editedmessage", make_column("userId", &DeletedMessage::userId),
make_column("userId", &EditedMessage::get_user_id, &EditedMessage::set_user_id), make_column("dialogId", &DeletedMessage::dialogId),
make_column("dialogId", &EditedMessage::get_dialog_id, &EditedMessage::set_dialog_id), make_column("groupedId", &DeletedMessage::groupedId),
make_column("groupedId", &EditedMessage::get_grouped_id, &EditedMessage::set_grouped_id), make_column("peerId", &DeletedMessage::peerId),
make_column("peerId", &EditedMessage::get_peer_id, &EditedMessage::set_peer_id), make_column("fromId", &DeletedMessage::fromId),
make_column("fromId", &EditedMessage::get_from_id, &EditedMessage::set_from_id), make_column("topicId", &DeletedMessage::topicId),
make_column("topicId", &EditedMessage::get_topic_id, &EditedMessage::set_topic_id), make_column("messageId", &DeletedMessage::messageId),
make_column("messageId", &EditedMessage::get_message_id, &EditedMessage::set_message_id), make_column("date", &DeletedMessage::date),
make_column("date", &EditedMessage::get_date, &EditedMessage::set_date), make_column("flags", &DeletedMessage::flags),
make_column("flags", &EditedMessage::get_flags, &EditedMessage::set_flags), make_column("editDate", &DeletedMessage::editDate),
make_column("editDate", &EditedMessage::get_edit_date, &EditedMessage::set_edit_date), make_column("views", &DeletedMessage::views),
make_column("editHide", &EditedMessage::is_edit_hide, &EditedMessage::set_edit_hide), make_column("fwdFlags", &DeletedMessage::fwdFlags),
make_column("out", &EditedMessage::is_out, &EditedMessage::set_out), make_column("fwdFromId", &DeletedMessage::fwdFromId),
make_column("entityCreateDate", &EditedMessage::get_entity_create_date, &EditedMessage::set_entity_create_date), make_column("fwdName", &DeletedMessage::fwdName),
make_column("text", &EditedMessage::get_text, &EditedMessage::set_text), make_column("fwdDate", &DeletedMessage::fwdDate),
make_column("textEntities", &EditedMessage::get_text_entities, &EditedMessage::set_text_entities), make_column("fwdPostAuthor", &DeletedMessage::fwdPostAuthor),
make_column("mediaPath", &EditedMessage::get_media_path, &EditedMessage::set_media_path), make_column("replyFlags", &DeletedMessage::replyFlags),
make_column("documentType", &EditedMessage::get_document_type, &EditedMessage::set_document_type), make_column("replyMessageId", &DeletedMessage::replyMessageId),
make_column("documentSerialized", &EditedMessage::get_document_serialized, make_column("replyPeerId", &DeletedMessage::replyPeerId),
&EditedMessage::set_document_serialized), make_column("replyTopId", &DeletedMessage::replyTopId),
make_column("thumbsSerialized", &EditedMessage::get_thumbs_serialized, &EditedMessage::set_thumbs_serialized), make_column("replyForumTopic", &DeletedMessage::replyForumTopic),
make_column("documentAttributesSerialized", &EditedMessage::get_document_attributes_serialized, make_column("entityCreateDate", &DeletedMessage::entityCreateDate),
&EditedMessage::set_document_attributes_serialized), make_column("text", &DeletedMessage::text),
make_column("mimeType", &EditedMessage::get_mime_type, &EditedMessage::set_mime_type) make_column("textEntities", &DeletedMessage::textEntities),
make_column("mediaPath", &DeletedMessage::mediaPath),
make_column("hqThumbPath", &DeletedMessage::hqThumbPath),
make_column("documentType", &DeletedMessage::documentType),
make_column("documentSerialized", &DeletedMessage::documentSerialized),
make_column("thumbsSerialized", &DeletedMessage::thumbsSerialized),
make_column("documentAttributesSerialized",
&DeletedMessage::documentAttributesSerialized),
make_column("mimeType", &DeletedMessage::mimeType)
), ),
make_table( make_table("EditedMessage",
"deletedmessage", make_column("userId", &EditedMessage::userId),
make_column("userId", &DeletedMessage::get_user_id, &DeletedMessage::set_user_id), make_column("dialogId", &EditedMessage::dialogId),
make_column("dialogId", &DeletedMessage::get_dialog_id, &DeletedMessage::set_dialog_id), make_column("groupedId", &EditedMessage::groupedId),
make_column("groupedId", &DeletedMessage::get_grouped_id, &DeletedMessage::set_grouped_id), make_column("peerId", &EditedMessage::peerId),
make_column("peerId", &DeletedMessage::get_peer_id, &DeletedMessage::set_peer_id), make_column("fromId", &EditedMessage::fromId),
make_column("fromId", &DeletedMessage::get_from_id, &DeletedMessage::set_from_id), make_column("topicId", &EditedMessage::topicId),
make_column("topicId", &DeletedMessage::get_topic_id, &DeletedMessage::set_topic_id), make_column("messageId", &EditedMessage::messageId),
make_column("messageId", &DeletedMessage::get_message_id, &DeletedMessage::set_message_id), make_column("date", &EditedMessage::date),
make_column("date", &DeletedMessage::get_date, &DeletedMessage::set_date), make_column("flags", &EditedMessage::flags),
make_column("flags", &DeletedMessage::get_flags, &DeletedMessage::set_flags), make_column("editDate", &EditedMessage::editDate),
make_column("editDate", &DeletedMessage::get_edit_date, &DeletedMessage::set_edit_date), make_column("views", &EditedMessage::views),
make_column("editHide", &DeletedMessage::is_edit_hide, &DeletedMessage::set_edit_hide), make_column("fwdFlags", &EditedMessage::fwdFlags),
make_column("out", &DeletedMessage::is_out, &DeletedMessage::set_out), make_column("fwdFromId", &EditedMessage::fwdFromId),
make_column("entityCreateDate", &DeletedMessage::get_entity_create_date, make_column("fwdName", &EditedMessage::fwdName),
&DeletedMessage::set_entity_create_date), make_column("fwdDate", &EditedMessage::fwdDate),
make_column("text", &DeletedMessage::get_text, &DeletedMessage::set_text), make_column("fwdPostAuthor", &EditedMessage::fwdPostAuthor),
make_column("textEntities", &DeletedMessage::get_text_entities, &DeletedMessage::set_text_entities), make_column("replyFlags", &EditedMessage::replyFlags),
make_column("mediaPath", &DeletedMessage::get_media_path, &DeletedMessage::set_media_path), make_column("replyMessageId", &EditedMessage::replyMessageId),
make_column("documentType", &DeletedMessage::get_document_type, &DeletedMessage::set_document_type), make_column("replyPeerId", &EditedMessage::replyPeerId),
make_column("documentSerialized", &DeletedMessage::get_document_serialized, make_column("replyTopId", &EditedMessage::replyTopId),
&DeletedMessage::set_document_serialized), make_column("replyForumTopic", &EditedMessage::replyForumTopic),
make_column("thumbsSerialized", &DeletedMessage::get_thumbs_serialized, &DeletedMessage::set_thumbs_serialized), make_column("entityCreateDate", &EditedMessage::entityCreateDate),
make_column("documentAttributesSerialized", &DeletedMessage::get_document_attributes_serialized, make_column("text", &EditedMessage::text),
&DeletedMessage::set_document_attributes_serialized), make_column("textEntities", &EditedMessage::textEntities),
make_column("mimeType", &DeletedMessage::get_mime_type, &DeletedMessage::set_mime_type) make_column("mediaPath", &EditedMessage::mediaPath),
make_column("hqThumbPath", &EditedMessage::hqThumbPath),
make_column("documentType", &EditedMessage::documentType),
make_column("documentSerialized", &EditedMessage::documentSerialized),
make_column("thumbsSerialized", &EditedMessage::thumbsSerialized),
make_column("documentAttributesSerialized",
&EditedMessage::documentAttributesSerialized),
make_column("mimeType", &EditedMessage::mimeType)
),
make_table("DeletedDialog",
make_column("userId", &DeletedDialog::userId),
make_column("dialogId", &DeletedDialog::dialogId),
make_column("peerId", &DeletedDialog::peerId),
make_column("topMessage", &DeletedDialog::topMessage),
make_column("lastMessageDate", &DeletedDialog::lastMessageDate),
make_column("flags", &DeletedDialog::flags),
make_column("entityCreateDate", &DeletedDialog::entityCreateDate)
) )
); );
namespace AyuDatabase namespace AyuDatabase
{ {
void initialize() void initialize()
{ {
storage.sync_schema(); storage.sync_schema();
@ -90,9 +114,9 @@ namespace AyuDatabase
{ {
return storage.get_all<EditedMessage>( return storage.get_all<EditedMessage>(
where( where(
c(&EditedMessage::get_user_id) == userId and c(&EditedMessage::userId) == userId and
c(&EditedMessage::get_dialog_id) == dialogId and c(&EditedMessage::dialogId) == dialogId and
c(&EditedMessage::get_message_id) == messageId c(&EditedMessage::messageId) == messageId
) )
); );
} }
@ -101,10 +125,11 @@ namespace AyuDatabase
{ {
return storage.count<EditedMessage>( return storage.count<EditedMessage>(
where( where(
c(&EditedMessage::get_user_id) == userId and c(&EditedMessage::userId) == userId and
c(&EditedMessage::get_dialog_id) == dialogId and c(&EditedMessage::dialogId) == dialogId and
c(&EditedMessage::get_message_id) == messageId c(&EditedMessage::messageId) == messageId
) )
) > 0; ) > 0;
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "entities.h" #include "entities.h"
@ -13,9 +12,11 @@
namespace AyuDatabase namespace AyuDatabase
{ {
void initialize(); void initialize();
void addEditedMessage(const EditedMessage &message); void addEditedMessage(const EditedMessage &message);
std::vector<EditedMessage> getEditedMessages(ID userId, ID dialogId, ID messageId); std::vector<EditedMessage> getEditedMessages(ID userId, ID dialogId, ID messageId);
bool hasRevisions(ID userId, ID dialogId, ID messageId); bool hasRevisions(ID userId, ID dialogId, ID messageId);
} }

View file

@ -4,18 +4,17 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include <string> #include <string>
#define ID long long #define ID long long
// https://github.com/AyuGram/AyuGram4A/blob/rewrite/TMessagesProj/src/main/java/com/radolyn/ayugram/database/entities/AyuMessageBase.java template<typename TableName>
class AyuMessageBase class AyuMessageBase
{ {
public: public:
ID fakeId;
ID userId; ID userId;
ID dialogId; ID dialogId;
ID groupedId; ID groupedId;
@ -24,449 +23,43 @@ public:
ID topicId; ID topicId;
int messageId; int messageId;
int date; int date;
int flags; int flags;
int editDate; int editDate;
bool editHide; int views;
bool out; int fwdFlags;
ID fwdFromId;
std::string fwdName;
int fwdDate;
std::string fwdPostAuthor;
int replyFlags;
int replyMessageId;
ID replyPeerId;
int replyTopId;
bool replyForumTopic;
int entityCreateDate; int entityCreateDate;
std::string text;
std::string text; // plain text std::vector<char> textEntities;
std::string textEntities; // TL serialized std::string mediaPath;
std::string mediaPath; // full path std::string hqThumbPath;
int documentType; // see DOCUMENT_TYPE_* int documentType;
std::string documentSerialized; // for sticker; TL serialized std::vector<char> documentSerialized;
std::string thumbsSerialized; // for video/etc.; TL serialized std::vector<char> thumbsSerialized;
std::string documentAttributesSerialized; // for video/voice/etc.; TL serialized std::vector<char> documentAttributesSerialized;
std::string mimeType; std::string mimeType;
}; };
class DeletedMessage : public AyuMessageBase using DeletedMessage = AyuMessageBase<struct DeletedMessageTag>;
using EditedMessage = AyuMessageBase<struct EditedMessageTag>;
class DeletedDialog
{ {
public: public:
[[nodiscard]] long long get_user_id() const ID fakeId;
{ ID userId;
return userId; ID dialogId;
} ID peerId;
int topMessage;
void set_user_id(long long user_id) int lastMessageDate;
{ int flags;
userId = user_id; int entityCreateDate;
}
[[nodiscard]] long long get_dialog_id() const
{
return dialogId;
}
void set_dialog_id(long long dialog_id)
{
dialogId = dialog_id;
}
[[nodiscard]] long long get_grouped_id() const
{
return groupedId;
}
void set_grouped_id(long long grouped_id)
{
groupedId = grouped_id;
}
[[nodiscard]] long long get_peer_id() const
{
return peerId;
}
void set_peer_id(long long peer_id)
{
peerId = peer_id;
}
[[nodiscard]] long long get_from_id() const
{
return fromId;
}
void set_from_id(long long from_id)
{
fromId = from_id;
}
[[nodiscard]] long long get_topic_id() const
{
return topicId;
}
void set_topic_id(long long topic_id)
{
topicId = topic_id;
}
[[nodiscard]] int get_message_id() const
{
return messageId;
}
void set_message_id(int message_id)
{
messageId = message_id;
}
[[nodiscard]] int get_date() const
{
return date;
}
void set_date(int date)
{
this->date = date;
}
[[nodiscard]] int get_flags() const
{
return flags;
}
void set_flags(int flags)
{
this->flags = flags;
}
[[nodiscard]] int get_edit_date() const
{
return editDate;
}
void set_edit_date(int edit_date)
{
editDate = edit_date;
}
[[nodiscard]] bool is_edit_hide() const
{
return editHide;
}
void set_edit_hide(bool edit_hide)
{
editHide = edit_hide;
}
[[nodiscard]] bool is_out() const
{
return out;
}
void set_out(bool out)
{
this->out = out;
}
[[nodiscard]] int get_entity_create_date() const
{
return entityCreateDate;
}
void set_entity_create_date(int entity_create_date)
{
entityCreateDate = entity_create_date;
}
[[nodiscard]] std::string get_text() const
{
return text;
}
void set_text(const std::string& text)
{
this->text = text;
}
[[nodiscard]] std::string get_text_entities() const
{
return textEntities;
}
void set_text_entities(const std::string& text_entities)
{
textEntities = text_entities;
}
[[nodiscard]] std::string get_media_path() const
{
return mediaPath;
}
void set_media_path(const std::string& media_path)
{
mediaPath = media_path;
}
[[nodiscard]] int get_document_type() const
{
return documentType;
}
void set_document_type(int document_type)
{
documentType = document_type;
}
[[nodiscard]] std::string get_document_serialized() const
{
return documentSerialized;
}
void set_document_serialized(const std::string& document_serialized)
{
documentSerialized = document_serialized;
}
[[nodiscard]] std::string get_thumbs_serialized() const
{
return thumbsSerialized;
}
void set_thumbs_serialized(const std::string& thumbs_serialized)
{
thumbsSerialized = thumbs_serialized;
}
[[nodiscard]] std::string get_document_attributes_serialized() const
{
return documentAttributesSerialized;
}
void set_document_attributes_serialized(const std::string& document_attributes_serialized)
{
documentAttributesSerialized = document_attributes_serialized;
}
[[nodiscard]] std::string get_mime_type() const
{
return mimeType;
}
void set_mime_type(const std::string& mime_type)
{
mimeType = mime_type;
}
};
class EditedMessage : public AyuMessageBase
{
public:
[[nodiscard]] long long get_user_id() const
{
return userId;
}
void set_user_id(long long user_id)
{
userId = user_id;
}
[[nodiscard]] long long get_dialog_id() const
{
return dialogId;
}
void set_dialog_id(long long dialog_id)
{
dialogId = dialog_id;
}
[[nodiscard]] long long get_grouped_id() const
{
return groupedId;
}
void set_grouped_id(long long grouped_id)
{
groupedId = grouped_id;
}
[[nodiscard]] long long get_peer_id() const
{
return peerId;
}
void set_peer_id(long long peer_id)
{
peerId = peer_id;
}
[[nodiscard]] long long get_from_id() const
{
return fromId;
}
void set_from_id(long long from_id)
{
fromId = from_id;
}
[[nodiscard]] long long get_topic_id() const
{
return topicId;
}
void set_topic_id(long long topic_id)
{
topicId = topic_id;
}
[[nodiscard]] int get_message_id() const
{
return messageId;
}
void set_message_id(int message_id)
{
messageId = message_id;
}
[[nodiscard]] int get_date() const
{
return date;
}
void set_date(int date)
{
this->date = date;
}
[[nodiscard]] int get_flags() const
{
return flags;
}
void set_flags(int flags)
{
this->flags = flags;
}
[[nodiscard]] int get_edit_date() const
{
return editDate;
}
void set_edit_date(int edit_date)
{
editDate = edit_date;
}
[[nodiscard]] bool is_edit_hide() const
{
return editHide;
}
void set_edit_hide(bool edit_hide)
{
editHide = edit_hide;
}
[[nodiscard]] bool is_out() const
{
return out;
}
void set_out(bool out)
{
this->out = out;
}
[[nodiscard]] int get_entity_create_date() const
{
return entityCreateDate;
}
void set_entity_create_date(int entity_create_date)
{
entityCreateDate = entity_create_date;
}
[[nodiscard]] std::string get_text() const
{
return text;
}
void set_text(const std::string& text)
{
this->text = text;
}
[[nodiscard]] std::string get_text_entities() const
{
return textEntities;
}
void set_text_entities(const std::string& text_entities)
{
textEntities = text_entities;
}
[[nodiscard]] std::string get_media_path() const
{
return mediaPath;
}
void set_media_path(const std::string& media_path)
{
mediaPath = media_path;
}
[[nodiscard]] int get_document_type() const
{
return documentType;
}
void set_document_type(int document_type)
{
documentType = document_type;
}
[[nodiscard]] std::string get_document_serialized() const
{
return documentSerialized;
}
void set_document_serialized(const std::string& document_serialized)
{
documentSerialized = document_serialized;
}
[[nodiscard]] std::string get_thumbs_serialized() const
{
return thumbsSerialized;
}
void set_thumbs_serialized(const std::string& thumbs_serialized)
{
thumbsSerialized = thumbs_serialized;
}
[[nodiscard]] std::string get_document_attributes_serialized() const
{
return documentAttributesSerialized;
}
void set_document_attributes_serialized(const std::string& document_attributes_serialized)
{
documentAttributesSerialized = document_attributes_serialized;
}
[[nodiscard]] std::string get_mime_type() const
{
return mimeType;
}
void set_mime_type(const std::string& mime_type)
{
mimeType = mime_type;
}
}; };

View file

@ -4,16 +4,17 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
namespace AyuFeatures::StreamerMode namespace AyuFeatures::StreamerMode
{ {
bool isEnabled(); bool isEnabled();
void enable(); void enable();
void disable(); void disable();
void hideWidgetWindow(QWidget *widget); void hideWidgetWindow(QWidget *widget);
void showWidgetWindow(QWidget *widget); void showWidgetWindow(QWidget *widget);
} }

View file

@ -14,6 +14,7 @@
namespace AyuFeatures::StreamerMode namespace AyuFeatures::StreamerMode
{ {
bool isEnabledVal; bool isEnabledVal;
bool isEnabled() bool isEnabled()
@ -31,13 +32,16 @@ namespace AyuFeatures::StreamerMode
isEnabledVal = false; isEnabledVal = false;
} }
void hideWidgetWindow(QWidget* widget) { void hideWidgetWindow(QWidget *widget)
{
} }
void showWidgetWindow(QWidget* widget) { void showWidgetWindow(QWidget *widget)
{
} }
} }
#endif #endif

View file

@ -14,6 +14,7 @@
namespace AyuFeatures::StreamerMode namespace AyuFeatures::StreamerMode
{ {
bool isEnabledVal; bool isEnabledVal;
bool isEnabled() bool isEnabled()
@ -48,6 +49,7 @@ namespace AyuFeatures::StreamerMode
auto handle = reinterpret_cast<HWND>(widget->window()->winId()); auto handle = reinterpret_cast<HWND>(widget->window()->winId());
SetWindowDisplayAffinity(handle, WDA_NONE); SetWindowDisplayAffinity(handle, WDA_NONE);
} }
} }
#endif #endif

View file

@ -9,6 +9,7 @@
#include "ayu/ayu_constants.h" #include "ayu/ayu_constants.h"
#include "ayu/database/ayu_database.h" #include "ayu/database/ayu_database.h"
#include "ayu/utils/ayu_mapper.h"
#include "ayu/utils/telegram_helpers.h" #include "ayu/utils/telegram_helpers.h"
#include "base/unixtime.h" #include "base/unixtime.h"
@ -18,17 +19,18 @@
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_item_components.h"
#include "main/main_session.h" #include "main/main_session.h"
namespace AyuMessages namespace AyuMessages
{ {
std::optional<ayu_messages_controller> controller = std::nullopt; std::optional<ayu_messages_controller> controller = std::nullopt;
void initialize() void initialize()
{ {
if (controller.has_value()) if (controller.has_value()) {
{
return; return;
} }
@ -41,30 +43,32 @@ namespace AyuMessages
return controller.value(); return controller.value();
} }
void map(HistoryMessageEdition& edition, not_null<HistoryItem*> item, AyuMessageBase& message) void map(HistoryMessageEdition &edition, not_null<HistoryItem *> item, EditedMessage &message)
{ {
message.userId = item->history()->owner().session().userId().bare; message.userId = item->history()->owner().session().userId().bare;
message.dialogId = getDialogIdFromPeer(item->history()->peer); message.dialogId = getDialogIdFromPeer(item->history()->peer);
message.groupedId = item->groupId().value; message.groupedId = item->groupId().value;
message.peerId = item->from()->id.value; // todo: ??? message.peerId = item->from()->id.value; // todo: ???
message.fromId = item->from()->id.value; message.fromId = item->from()->id.value;
if (auto topic = item->history()->asTopic()) if (auto topic = item->history()->asTopic()) {
{
message.topicId = topic->rootId().bare; message.topicId = topic->rootId().bare;
} }
message.messageId = item->id.bare; message.messageId = item->id.bare;
message.date = item->date(); message.date = item->date();
message.flags = AyuMapper::mapItemFlagsToMTPFlags(item);
// message.flags = todo: if (auto edited = item->Get<HistoryMessageEdited>()) {
message.editDate = edited->date;
} else {
message.editDate = base::unixtime::now();
}
message.editDate = edition.editDate; message.views = item->viewsCount();
message.editHide = item->hideEditedBadge();
message.out = item->out();
message.entityCreateDate = base::unixtime::now(); // todo: rework message.entityCreateDate = base::unixtime::now(); // todo: rework
auto serializedText = serializeTextWithEntities(item); auto serializedText = serializeTextWithEntities(item);
message.text = serializedText.first; message.text = serializedText.first;
message.textEntities = serializedText.second; // message.textEntities = serializedText.second;
// todo: // todo:
message.mediaPath = "/"; message.mediaPath = "/";
@ -101,4 +105,5 @@ namespace AyuMessages
return AyuDatabase::hasRevisions(userId, dialogId, msgId); return AyuDatabase::hasRevisions(userId, dialogId, msgId);
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ayu/database/entities.h" #include "ayu/database/entities.h"
@ -13,6 +12,7 @@
namespace AyuMessages namespace AyuMessages
{ {
class ayu_messages_controller class ayu_messages_controller
{ {
public: public:
@ -22,4 +22,5 @@ namespace AyuMessages
}; };
ayu_messages_controller &getInstance(); ayu_messages_controller &getInstance();
} }

View file

@ -24,6 +24,7 @@
namespace AyuSync namespace AyuSync
{ {
std::optional<ayu_sync_controller> controller = std::nullopt; std::optional<ayu_sync_controller> controller = std::nullopt;
bool isAgentDownloaded() bool isAgentDownloaded()
@ -38,8 +39,7 @@ namespace AyuSync
void initialize() void initialize()
{ {
if (controller.has_value()) if (controller.has_value()) {
{
return; return;
} }
@ -54,13 +54,11 @@ namespace AyuSync
void ayu_sync_controller::initializeAgent() void ayu_sync_controller::initializeAgent()
{ {
if (!isAgentDownloaded()) if (!isAgentDownloaded()) {
{
return; return;
} }
if (isAgentRunning()) if (isAgentRunning()) {
{
killProcess(AgentFilename); killProcess(AgentFilename);
} }
@ -76,8 +74,7 @@ namespace AyuSync
void ayu_sync_controller::syncRead(not_null<History *> history, MsgId untilId) void ayu_sync_controller::syncRead(not_null<History *> history, MsgId untilId)
{ {
if (!initialized) if (!initialized) {
{
return; return;
} }
@ -98,11 +95,9 @@ namespace AyuSync
LOG(("Pipe created")); LOG(("Pipe created"));
while (true) while (true) {
{
auto p = pipe->receive(); auto p = pipe->receive();
if (p == std::nullopt) if (p == std::nullopt) {
{
continue; continue;
} }
@ -122,28 +117,23 @@ namespace AyuSync
LOG(("userId: %1, type: %2").arg(userId).arg(type.c_str())); LOG(("userId: %1, type: %2").arg(userId).arg(type.c_str()));
if (!accountExists(userId)) if (!accountExists(userId)) {
{
LOG(("Sync for unknown account: %1").arg(userId)); LOG(("Sync for unknown account: %1").arg(userId));
return; return;
} }
if (type == "sync_force") if (type == "sync_force") {
{
auto ev = p.get<SyncForce>(); auto ev = p.get<SyncForce>();
onSyncForce(ev); onSyncForce(ev);
} }
else if (type == "sync_batch") else if (type == "sync_batch") {
{
onSyncBatch(p); onSyncBatch(p);
} }
else if (type == "sync_read") else if (type == "sync_read") {
{
auto ev = p.get<SyncRead>(); auto ev = p.get<SyncRead>();
onSyncRead(ev); onSyncRead(ev);
} }
else else {
{
LOG(("Unknown sync type: %1").arg(type.c_str())); LOG(("Unknown sync type: %1").arg(type.c_str()));
} }
} }
@ -156,10 +146,8 @@ namespace AyuSync
SyncBatch readsBatchEvent; SyncBatch readsBatchEvent;
readsBatchEvent.userId = ev.userId; readsBatchEvent.userId = ev.userId;
for (const auto& row : histories->indexed()->all()) for (const auto &row : histories->indexed()->all()) {
{ if (const auto history = row->history()) {
if (const auto history = row->history())
{
auto dialogId = getDialogIdFromPeer(history->peer); auto dialogId = getDialogIdFromPeer(history->peer);
SyncRead readEv; SyncRead readEv;
@ -168,8 +156,7 @@ namespace AyuSync
history->calculateFirstUnreadMessage(); history->calculateFirstUnreadMessage();
auto unreadElement = history->firstUnreadMessage(); auto unreadElement = history->firstUnreadMessage();
if (!unreadElement && history->unreadCount()) if (!unreadElement && history->unreadCount()) {
{
LOG(("No unread can be calculated for %1").arg(dialogId)); LOG(("No unread can be calculated for %1").arg(dialogId));
continue; continue;
} }
@ -195,8 +182,7 @@ namespace AyuSync
void ayu_sync_controller::onSyncBatch(json ev) void ayu_sync_controller::onSyncBatch(json ev)
{ {
for (auto& item : ev["args"]["events"]) for (auto &item : ev["args"]["events"]) {
{
invokeHandler(item); invokeHandler(item);
} }
} }
@ -208,14 +194,13 @@ namespace AyuSync
auto session = getSession(ev.userId); auto session = getSession(ev.userId);
auto history = getHistoryFromDialogId(ev.args.dialogId, session); auto history = getHistoryFromDialogId(ev.args.dialogId, session);
if (history->folderKnown()) if (history->folderKnown()) {
{
history->inboxRead(ev.args.untilId, ev.args.unread); history->inboxRead(ev.args.untilId, ev.args.unread);
} }
else else {
{
LOG(("Unknown dialog %1").arg(ev.args.dialogId)); LOG(("Unknown dialog %1").arg(ev.args.dialogId));
} }
}); });
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "models.h" #include "models.h"
@ -16,17 +15,21 @@
using json = nlohmann::json; using json = nlohmann::json;
const std::string AgentFilename =
#ifdef _WIN32 #ifdef _WIN32
"AyuSync.Agent.exe";
const std::string AgentFilename = "AyuSync.Agent.exe";
#else #else
"AyuSync.Agent";
const std::string AgentFilename = "AyuSync.Agent";
#endif #endif
const std::string AgentPath = "./AyuSync/" + AgentFilename; const std::string AgentPath = "./AyuSync/" + AgentFilename;
namespace AyuSync namespace AyuSync
{ {
class ayu_sync_controller class ayu_sync_controller
{ {
public: public:
@ -51,4 +54,5 @@ namespace AyuSync
bool isAgentDownloaded(); bool isAgentDownloaded();
bool isAgentRunning(); bool isAgentRunning();
} }

View file

@ -1,10 +1,15 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ayu/libs/json.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
#include "ayu/libs/json.hpp"
#include "ayu/database/entities.h" #include "ayu/database/entities.h"
using json = nlohmann::json; using json = nlohmann::json;
@ -30,7 +35,41 @@ public:
std::vector<json> events; std::vector<json> events;
}; };
SyncBatchArgs args; SyncBatchArgs args{};
};
class SyncForce : public SyncEvent
{
public:
explicit SyncForce()
{
type = "sync_force";
}
class SyncForceArgs
{
public:
int fromDate;
};
SyncForceArgs args{};
};
class SyncForceFinish : public SyncEvent
{
public:
explicit SyncForceFinish()
{
type = "sync_force_finish";
}
class SyncForceFinishArgs
{
public:
short dummy; // required to be JSON serializable
};
SyncForceFinishArgs args{};
}; };
class SyncRead : public SyncEvent class SyncRead : public SyncEvent
@ -49,50 +88,53 @@ public:
int unread; int unread;
}; };
SyncReadArgs args; SyncReadArgs args{};
}; };
class SyncForce : public SyncEvent class SyncDeletedMessage : public SyncEvent
{ {
public: public:
explicit SyncForce() explicit SyncDeletedMessage()
{ {
type = "sync_force"; type = "sync_deleted_message";
} }
class SyncForceArgs class SyncDeletedMessageArgs
{ {
public: public:
int fromDate; json message;
}; };
SyncForceArgs args; SyncDeletedMessageArgs args{};
}; };
class SyncForceFinish : public SyncEvent class SyncEditedMessage : public SyncEvent
{ {
public: public:
explicit SyncForceFinish() explicit SyncEditedMessage()
{ {
type = "sync_force_finish"; type = "sync_edited_message";
} }
class SyncForceFinishArgs class SyncEditedMessageArgs
{ {
public: public:
short dummy; // required to be JSON serializable json message;
}; };
SyncForceFinishArgs args; SyncEditedMessageArgs args{};
}; };
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncEvent, type, userId) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncEvent, type, userId)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncBatch::SyncBatchArgs, events) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncBatch::SyncBatchArgs, events)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncBatch, type, userId, args) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncBatch, type, userId, args)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncRead::SyncReadArgs, dialogId, untilId, unread)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncRead, type, userId, args)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForce::SyncForceArgs, fromDate) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForce::SyncForceArgs, fromDate)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForce, type, userId, args) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForce, type, userId, args)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForceFinish::SyncForceFinishArgs, dummy) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForceFinish::SyncForceFinishArgs, dummy)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForceFinish, type, userId, args) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncForceFinish, type, userId, args)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncRead::SyncReadArgs, dialogId, untilId, unread)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncRead, type, userId, args)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncDeletedMessage::SyncDeletedMessageArgs, message)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncDeletedMessage, type, userId, args)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncEditedMessage::SyncEditedMessageArgs, message)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SyncEditedMessage, type, userId, args)

View file

@ -32,8 +32,7 @@ void ayu_pipe_wrapper::send(json p)
std::optional<json> ayu_pipe_wrapper::receive() std::optional<json> ayu_pipe_wrapper::receive()
{ {
if (!is->is_open()) if (!is->is_open()) {
{
return std::nullopt; return std::nullopt;
} }
@ -44,16 +43,14 @@ std::optional<json> ayu_pipe_wrapper::receive()
LOG(("ayu_pipe_wrapper::receive() length: %1").arg(length)); LOG(("ayu_pipe_wrapper::receive() length: %1").arg(length));
if (length <= 0) if (length <= 0) {
{
return std::nullopt; return std::nullopt;
} }
auto sb = stringbuf(); auto sb = stringbuf();
unsigned char buff[4096]; unsigned char buff[4096];
while (length > 0) while (length > 0) {
{
auto readSize = std::min(length, static_cast<int>(sizeof(buff))); auto readSize = std::min(length, static_cast<int>(sizeof(buff)));
is->read(buff, readSize); is->read(buff, readSize);

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ayu/libs/json.hpp" #include "ayu/libs/json.hpp"

View file

@ -1,3 +1,10 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include <codecvt> #include <codecvt>
#include <iostream> #include <iostream>
#include <locale> #include <locale>
@ -17,26 +24,22 @@ inline bool isProcessRunning(const std::string& name)
#ifdef _WIN32 #ifdef _WIN32
// Create a snapshot of all processes // Create a snapshot of all processes
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) if (snapshot == INVALID_HANDLE_VALUE) {
{
std::cerr << "Failed to create snapshot\n"; std::cerr << "Failed to create snapshot\n";
return false; return false;
} }
// Iterate over the processes and compare the names // Iterate over the processes and compare the names
PROCESSENTRY32 entry; PROCESSENTRY32 entry;
entry.dwSize = sizeof(entry); entry.dwSize = sizeof(entry);
if (!Process32First(snapshot, &entry)) if (!Process32First(snapshot, &entry)) {
{
std::cerr << "Failed to get first process\n"; std::cerr << "Failed to get first process\n";
CloseHandle(snapshot); CloseHandle(snapshot);
return false; return false;
} }
do do {
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string entry_name = converter.to_bytes(entry.szExeFile); std::string entry_name = converter.to_bytes(entry.szExeFile);
if (name == entry_name) if (name == entry_name) {
{
// Found a match // Found a match
CloseHandle(snapshot); CloseHandle(snapshot);
return true; return true;
@ -56,30 +59,25 @@ void killProcess(const std::string& name)
{ {
#ifdef _WIN32 #ifdef _WIN32
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) if (snapshot == INVALID_HANDLE_VALUE) {
{
std::cerr << "Failed to create snapshot\n"; std::cerr << "Failed to create snapshot\n";
return; return;
} }
// Iterate over the processes and compare the names // Iterate over the processes and compare the names
PROCESSENTRY32 entry; PROCESSENTRY32 entry;
entry.dwSize = sizeof(entry); entry.dwSize = sizeof(entry);
if (!Process32First(snapshot, &entry)) if (!Process32First(snapshot, &entry)) {
{
std::cerr << "Failed to get first process\n"; std::cerr << "Failed to get first process\n";
CloseHandle(snapshot); CloseHandle(snapshot);
return; return;
} }
do do {
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string entry_name = converter.to_bytes(entry.szExeFile); std::string entry_name = converter.to_bytes(entry.szExeFile);
if (name == entry_name) if (name == entry_name) {
{
// Found a match // Found a match
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, entry.th32ProcessID); HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, entry.th32ProcessID);
if (hProcess != nullptr) if (hProcess != nullptr) {
{
TerminateProcess(hProcess, 9); TerminateProcess(hProcess, 9);
CloseHandle(hProcess); CloseHandle(hProcess);
} }

View file

@ -9,10 +9,13 @@
#include "ayu/ayu_settings.h" #include "ayu/ayu_settings.h"
static QString LAST_LOADED_NAME; static QString LAST_LOADED_NAME;
static QImage LAST_LOADED; static QImage LAST_LOADED;
static QImage LAST_LOADED_NO_MARGIN; static QImage LAST_LOADED_NO_MARGIN;
void loadAppIco() { void loadAppIco()
{
auto settings = &AyuSettings::getInstance(); auto settings = &AyuSettings::getInstance();
QString appDataPath = QDir::fromNativeSeparators(qgetenv("APPDATA")); QString appDataPath = QDir::fromNativeSeparators(qgetenv("APPDATA"));
@ -31,8 +34,7 @@ void loadAppIco() {
void loadIcons() void loadIcons()
{ {
auto settings = &AyuSettings::getInstance(); auto settings = &AyuSettings::getInstance();
if (LAST_LOADED_NAME != settings->appIcon) if (LAST_LOADED_NAME != settings->appIcon) {
{
LAST_LOADED_NAME = settings->appIcon; LAST_LOADED_NAME = settings->appIcon;
LAST_LOADED = QImage(qsl(":/gui/art/ayu/%1/logo256.png").arg(settings->appIcon)); LAST_LOADED = QImage(qsl(":/gui/art/ayu/%1/logo256.png").arg(settings->appIcon));

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
void loadAppIco(); void loadAppIco();

View file

@ -9,10 +9,12 @@
namespace AyuUi namespace AyuUi
{ {
std::unique_ptr<Lottie::Icon> getLottie(const QString &text) std::unique_ptr<Lottie::Icon> getLottie(const QString &text)
{ {
// todo: some kind of mapping // todo: some kind of mapping
// Lottie::MakeIcon({.json = QString(), .sizeOverride = {24, 24}}); // Lottie::MakeIcon({.json = QString(), .sizeOverride = {24, 24}});
return nullptr; return nullptr;
} }
} }

View file

@ -4,12 +4,13 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "lottie/lottie_icon.h" #include "lottie/lottie_icon.h"
namespace AyuUi namespace AyuUi
{ {
std::unique_ptr<Lottie::Icon> getLottie(const QString &text); std::unique_ptr<Lottie::Icon> getLottie(const QString &text);
} }

View file

@ -17,9 +17,11 @@
namespace AyuUi namespace AyuUi
{ {
ConfirmationBox::ConfirmationBox( ConfirmationBox::ConfirmationBox(
QWidget *, QWidget *,
not_null<Window::SessionController*> controller) : _controller(controller) not_null<Window::SessionController *> controller)
: _controller(controller)
{ {
// //
} }
@ -39,7 +41,8 @@ namespace AyuUi
ReadAllPeers(); ReadAllPeers();
closeBox(); closeBox();
}); });
addButton(tr::lng_cancel(), [=, this] { closeBox(); }); addButton(tr::lng_cancel(), [=, this]
{ closeBox(); });
} }
void ConfirmationBox::resizeEvent(QResizeEvent *e) void ConfirmationBox::resizeEvent(QResizeEvent *e)
@ -61,4 +64,5 @@ namespace AyuUi
settings->set_sendReadMessages(prev); settings->set_sendReadMessages(prev);
} }
} }

View file

@ -4,12 +4,14 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once
#include "ui/layers/box_content.h" #include "ui/layers/box_content.h"
#include "window/window_main_menu.h" #include "window/window_main_menu.h"
namespace AyuUi namespace AyuUi
{ {
class ConfirmationBox : public Ui::BoxContent class ConfirmationBox : public Ui::BoxContent
{ {
public: public:
@ -26,4 +28,5 @@ namespace AyuUi
not_null<Window::SessionController *> _controller; not_null<Window::SessionController *> _controller;
object_ptr<Ui::FlatLabel> _text = {nullptr}; object_ptr<Ui::FlatLabel> _text = {nullptr};
}; };
} }

View file

@ -28,7 +28,8 @@
#include "ayu/ayu_settings.h" #include "ayu/ayu_settings.h"
EditDeletedMarkBox::EditDeletedMarkBox(QWidget*) : EditDeletedMarkBox::EditDeletedMarkBox(QWidget *)
:
_text( _text(
this, this,
st::defaultInputField, st::defaultInputField,
@ -47,12 +48,16 @@ void EditDeletedMarkBox::prepare()
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom(); newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWidth, newHeight); setDimensions(st::boxWidth, newHeight);
addLeftButton(tr::ayu_BoxActionReset(), [=] { _text->setText(defaultDeletedMark); }); addLeftButton(tr::ayu_BoxActionReset(), [=]
{ _text->setText(defaultDeletedMark); });
addButton(tr::lng_settings_save(), [=] { save(); }); addButton(tr::lng_settings_save(), [=]
addButton(tr::lng_cancel(), [=] { closeBox(); }); { save(); });
addButton(tr::lng_cancel(), [=]
{ closeBox(); });
connect(_text, &Ui::InputField::submitted, [=] { submit(); }); connect(_text, &Ui::InputField::submitted, [=]
{ submit(); });
} }
void EditDeletedMarkBox::setInnerFocus() void EditDeletedMarkBox::setInnerFocus()
@ -62,13 +67,11 @@ void EditDeletedMarkBox::setInnerFocus()
void EditDeletedMarkBox::submit() void EditDeletedMarkBox::submit()
{ {
if (_text->getLastText().trimmed().isEmpty()) if (_text->getLastText().trimmed().isEmpty()) {
{
_text->setFocus(); _text->setFocus();
_text->showError(); _text->showError();
} }
else else {
{
save(); save();
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "base/timer.h" #include "base/timer.h"

View file

@ -28,7 +28,8 @@
#include "ayu/ayu_settings.h" #include "ayu/ayu_settings.h"
EditEditedMarkBox::EditEditedMarkBox(QWidget*) : EditEditedMarkBox::EditEditedMarkBox(QWidget *)
:
_text( _text(
this, this,
st::defaultInputField, st::defaultInputField,
@ -37,7 +38,6 @@ EditEditedMarkBox::EditEditedMarkBox(QWidget*) :
{ {
} }
void EditEditedMarkBox::prepare() void EditEditedMarkBox::prepare()
{ {
const auto defaultEditedMark = tr::lng_edited(tr::now); const auto defaultEditedMark = tr::lng_edited(tr::now);
@ -48,14 +48,17 @@ void EditEditedMarkBox::prepare()
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom(); newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWidth, newHeight); setDimensions(st::boxWidth, newHeight);
addLeftButton(tr::ayu_BoxActionReset(), [=] { _text->setText(defaultEditedMark); }); addLeftButton(tr::ayu_BoxActionReset(), [=]
addButton(tr::lng_settings_save(), [=] { save(); }); { _text->setText(defaultEditedMark); });
addButton(tr::lng_cancel(), [=] { closeBox(); }); addButton(tr::lng_settings_save(), [=]
{ save(); });
addButton(tr::lng_cancel(), [=]
{ closeBox(); });
connect(_text, &Ui::InputField::submitted, [=] { submit(); }); connect(_text, &Ui::InputField::submitted, [=]
{ submit(); });
} }
void EditEditedMarkBox::setInnerFocus() void EditEditedMarkBox::setInnerFocus()
{ {
_text->setFocusFast(); _text->setFocusFast();
@ -63,13 +66,11 @@ void EditEditedMarkBox::setInnerFocus()
void EditEditedMarkBox::submit() void EditEditedMarkBox::submit()
{ {
if (_text->getLastText().trimmed().isEmpty()) if (_text->getLastText().trimmed().isEmpty()) {
{
_text->setFocus(); _text->setFocus();
_text->showError(); _text->showError();
} }
else else {
{
save(); save();
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "base/timer.h" #include "base/timer.h"

View file

@ -20,6 +20,7 @@ using namespace Settings;
namespace AyuUi namespace AyuUi
{ {
MessageHistoryBox::MessageHistoryBox(QWidget *, HistoryItem *item) MessageHistoryBox::MessageHistoryBox(QWidget *, HistoryItem *item)
: _content(this), _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll)) : _content(this), _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll))
{ {
@ -46,8 +47,7 @@ namespace AyuUi
_scroll->resize(width(), height() - st::boxPhotoPadding.top() - st::boxPadding.bottom()); _scroll->resize(width(), height() - st::boxPhotoPadding.top() - st::boxPadding.bottom());
_scroll->move(0, st::boxPadding.top()); _scroll->move(0, st::boxPadding.top());
if (_content) if (_content) {
{
_content->resize(_scroll->width(), _content->height()); _content->resize(_scroll->width(), _content->height());
} }
} }
@ -63,16 +63,15 @@ namespace AyuUi
void MessageHistoryBox::addEditedMessagesToLayout(HistoryItem *item) void MessageHistoryBox::addEditedMessagesToLayout(HistoryItem *item)
{ {
auto messages = AyuMessages::getInstance().getEditedMessages(item); auto messages = AyuMessages::getInstance().getEditedMessages(item);
if (messages.empty()) if (messages.empty()) {
{
return; return;
} }
for (const auto& message : messages) for (const auto &message : messages) {
{
AddSkip(_content); AddSkip(_content);
AddDividerText(_content, rpl::single(QString::fromStdString(message.text))); AddDividerText(_content, rpl::single(QString::fromStdString(message.text)));
AddSkip(_content); AddSkip(_content);
} }
} }
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "history/history_item.h" #include "history/history_item.h"
@ -14,6 +13,7 @@
namespace AyuUi namespace AyuUi
{ {
class MessageHistoryBox : public Ui::BoxContent class MessageHistoryBox : public Ui::BoxContent
{ {
public: public:
@ -34,4 +34,5 @@ namespace AyuUi
rpl::event_stream<int> _contentHeight; rpl::event_stream<int> _contentHeight;
}; };
} }

View file

@ -14,6 +14,7 @@
namespace AyuUi namespace AyuUi
{ {
void VoiceConfirmBox(not_null<Ui::GenericBox *> box, Ui::ConfirmBoxArgs &&args) void VoiceConfirmBox(not_null<Ui::GenericBox *> box, Ui::ConfirmBoxArgs &&args)
{ {
const auto weak = MakeWeak(box); const auto weak = MakeWeak(box);
@ -29,8 +30,7 @@ namespace AyuUi
v::text::take_marked(std::move(args.text)), v::text::take_marked(std::move(args.text)),
args.labelStyle ? *args.labelStyle : st::boxLabel), args.labelStyle ? *args.labelStyle : st::boxLabel),
st::boxPadding); st::boxPadding);
if (args.labelFilter) if (args.labelFilter) {
{
label->setClickHandlerFilter(std::move(args.labelFilter)); label->setClickHandlerFilter(std::move(args.labelFilter));
} }
}); });
@ -39,22 +39,18 @@ namespace AyuUi
{ {
return [=, confirmed = std::move(callback)]() return [=, confirmed = std::move(callback)]()
{ {
if (const auto callbackPtr = std::get_if<1>(&confirmed)) if (const auto callbackPtr = std::get_if<1>(&confirmed)) {
{ if (auto callback = (*callbackPtr)) {
if (auto callback = (*callbackPtr))
{
callback(); callback();
} }
} }
else if (const auto callbackPtr = std::get_if<2>(&confirmed)) else if (const auto callbackPtr = std::get_if<2>(&confirmed)) {
{ if (auto callback = (*callbackPtr)) {
if (auto callback = (*callbackPtr)) callback(crl::guard(weak, [=]
{ { weak->closeBox(); }));
callback(crl::guard(weak, [=] { weak->closeBox(); }));
} }
} }
else if (weak) else if (weak) {
{
weak->closeBox(); weak->closeBox();
} }
}; };
@ -75,19 +71,16 @@ namespace AyuUi
box->events( box->events(
) | start_with_next([=](not_null<QEvent *> e) ) | start_with_next([=](not_null<QEvent *> e)
{ {
if ((e->type() != QEvent::KeyPress) || !confirmButton) if ((e->type() != QEvent::KeyPress) || !confirmButton) {
{
return; return;
} }
const auto k = static_cast<QKeyEvent *>(e.get()); const auto k = static_cast<QKeyEvent *>(e.get());
if (k->key() == Qt::Key_Enter || k->key() == Qt::Key_Return) if (k->key() == Qt::Key_Enter || k->key() == Qt::Key_Return) {
{
confirmButton->clicked(Qt::KeyboardModifiers(), Qt::LeftButton); confirmButton->clicked(Qt::KeyboardModifiers(), Qt::LeftButton);
} }
}, box->lifetime()); }, box->lifetime());
if (!args.inform) if (!args.inform) {
{
const auto cancelButton = box->addButton( const auto cancelButton = box->addButton(
v::text::take_plain(std::move(args.cancelText), tr::lng_cancel()), v::text::take_plain(std::move(args.cancelText), tr::lng_cancel()),
crl::guard(weak, [=, c = prepareCallback(args.cancelled)]() crl::guard(weak, [=, c = prepareCallback(args.cancelled)]()
@ -104,8 +97,7 @@ namespace AyuUi
}), *lifetime); }), *lifetime);
} }
if (args.strictCancel) if (args.strictCancel) {
{
lifetime->destroy(); lifetime->destroy();
} }
} }
@ -122,4 +114,5 @@ namespace AyuUi
.inform = true, .inform = true,
}); });
} }
} // namespace AyuUi } // namespace AyuUi

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
@ -13,10 +12,12 @@
namespace AyuUi namespace AyuUi
{ {
void VoiceConfirmBox(not_null<Ui::GenericBox *> box, Ui::ConfirmBoxArgs &&args); void VoiceConfirmBox(not_null<Ui::GenericBox *> box, Ui::ConfirmBoxArgs &&args);
[[nodiscard]] object_ptr<Ui::GenericBox> MakeConfirmBox( [[nodiscard]] object_ptr<Ui::GenericBox> MakeConfirmBox(
Ui::ConfirmBoxArgs &&args); Ui::ConfirmBoxArgs &&args);
[[nodiscard]] object_ptr<Ui::GenericBox> MakeInformBox(v::text::data text); [[nodiscard]] object_ptr<Ui::GenericBox> MakeInformBox(v::text::data text);
} // namespace Ui } // namespace Ui

View file

@ -4,11 +4,9 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#include "ayu/ui/context_menu/context_menu.h"
#include "context_menu.h"
#include "lang_auto.h" #include "lang_auto.h"
#include "ayu/ayu_state.h" #include "ayu/ayu_state.h"
#include "ayu/database/ayu_database.h"
#include "ayu/messages/ayu_messages_controller.h" #include "ayu/messages/ayu_messages_controller.h"
#include "ayu/ui/boxes/message_history_box.h" #include "ayu/ui/boxes/message_history_box.h"
@ -17,8 +15,12 @@
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ayu/ui/sections/edited/edited_log_section.h"
namespace AyuUi namespace AyuUi
{ {
AyuPopupMenu::AyuPopupMenu(HistoryInner *parent) AyuPopupMenu::AyuPopupMenu(HistoryInner *parent)
{ {
_ayuSubMenu = std::make_unique<Ui::PopupMenu>(parent, st::popupMenuWithIcons); _ayuSubMenu = std::make_unique<Ui::PopupMenu>(parent, st::popupMenuWithIcons);
@ -26,12 +28,10 @@ namespace AyuUi
void AyuPopupMenu::addHistoryAction(HistoryItem *item) void AyuPopupMenu::addHistoryAction(HistoryItem *item)
{ {
if (AyuMessages::getInstance().hasRevisions(item)) if (AyuMessages::getInstance().hasRevisions(item)) {
{
_ayuSubMenu->addAction(tr::ayu_EditsHistoryMenuText(tr::now), [=] _ayuSubMenu->addAction(tr::ayu_EditsHistoryMenuText(tr::now), [=]
{ {
auto box = Box<MessageHistoryBox>(item); item->history()->session().tryResolveWindow()->showSection(std::make_shared<EditedLog::SectionMemento>(item->history()->peer, item));
show(std::move(box));
}, &st::menuIconInfo); }, &st::menuIconInfo);
} }
} }
@ -59,4 +59,5 @@ namespace AyuUi
history->session().data().histories().readInboxOnNewMessage(item); history->session().data().histories().readInboxOnNewMessage(item);
}, &st::menuIconShowInChat); }, &st::menuIconShowInChat);
} }
} // namespace AyuUi } // namespace AyuUi

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "mainwindow.h" #include "mainwindow.h"
@ -78,11 +77,12 @@
#include "history/view/history_view_context_menu.h" #include "history/view/history_view_context_menu.h"
#include <ayu/ayu_settings.h> #include "ayu/ayu_settings.h"
#include <styles/style_info.h> #include "styles/style_info.h"
namespace AyuUi namespace AyuUi
{ {
class AyuPopupMenu class AyuPopupMenu
{ {
public: public:
@ -96,4 +96,5 @@ namespace AyuUi
std::unique_ptr<Ui::PopupMenu> _ayuSubMenu; std::unique_ptr<Ui::PopupMenu> _ayuSubMenu;
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,312 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "history/view/history_view_element.h"
#include "ayu/ui/sections/edited/edited_log_item.h"
#include "ayu/ui/sections/edited/edited_log_section.h"
#include "menu/menu_antispam_validator.h"
#include "ui/rp_widget.h"
#include "ui/effects/animations.h"
#include "ui/widgets/tooltip.h"
#include "mtproto/sender.h"
#include "base/timer.h"
struct ChatRestrictionsInfo;
namespace Main
{
class Session;
} // namespace Main
namespace HistoryView
{
class Element;
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
} // namespace HistoryView
namespace Ui
{
class PopupMenu;
class ChatStyle;
struct PeerUserpicView;
} // namespace Ui
namespace Window
{
class SessionController;
} // namespace Window
namespace EditedLog
{
class SectionMemento;
class InnerWidget final
: public Ui::RpWidget, public Ui::AbstractTooltipShower, public HistoryView::ElementDelegate
{
public:
InnerWidget(
QWidget *parent,
not_null<Window::SessionController *> controller,
not_null<PeerData *> peer,
not_null<HistoryItem *> item);
[[nodiscard]] Main::Session &session() const;
[[nodiscard]] not_null<Ui::ChatTheme *> theme() const
{
return _theme.get();
}
[[nodiscard]] rpl::producer<int> scrollToSignal() const;
[[nodiscard]] not_null<PeerData *> channel() const
{
return _peer;
}
// Set the correct scroll position after being resized.
void restoreScrollPosition();
void resizeToWidth(int newWidth, int minHeight)
{
_minHeight = minHeight;
return TWidget::resizeToWidth(newWidth);
}
void saveState(not_null<SectionMemento *> memento);
void restoreState(not_null<SectionMemento *> memento);
// Ui::AbstractTooltipShower interface.
QString tooltipText() const override;
QPoint tooltipPos() const override;
bool tooltipWindowActive() const override;
// HistoryView::ElementDelegate interface.
HistoryView::Context elementContext() override;
bool elementUnderCursor(
not_null<const HistoryView::Element *> view) override;
[[nodiscard]] float64 elementHighlightOpacity(
not_null<const HistoryItem *> item) const override;
bool elementInSelectionMode() override;
bool elementIntersectsRange(
not_null<const HistoryView::Element *> view,
int from,
int till) override;
void elementStartStickerLoop(
not_null<const HistoryView::Element *> view) override;
void elementShowPollResults(
not_null<PollData *> poll,
FullMsgId context) override;
void elementOpenPhoto(
not_null<PhotoData *> photo,
FullMsgId context) override;
void elementOpenDocument(
not_null<DocumentData *> document,
FullMsgId context,
bool showInMediaView = false) override;
void elementCancelUpload(const FullMsgId &context) override;
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) override;
bool elementAnimationsPaused() override;
bool elementHideReply(
not_null<const HistoryView::Element *> view) override;
bool elementShownUnread(
not_null<const HistoryView::Element *> view) override;
void elementSendBotCommand(
const QString &command,
const FullMsgId &context) override;
void elementHandleViaClick(not_null<UserData *> bot) override;
bool elementIsChatWide() override;
not_null<Ui::PathShiftGradient *> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override;
void elementStartInteraction(
not_null<const HistoryView::Element *> view) override;
void elementStartPremium(
not_null<const HistoryView::Element *> view,
HistoryView::Element *replacing) override;
void elementCancelPremium(
not_null<const HistoryView::Element *> view) override;
QString elementAuthorRank(
not_null<const HistoryView::Element *> view) override;
~InnerWidget();
protected:
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
void enterEventHook(QEnterEvent *e) override;
void leaveEventHook(QEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private:
using Element = HistoryView::Element;
enum class Direction
{
Up,
Down,
};
enum class MouseAction
{
None,
PrepareDrag,
Dragging,
Selecting,
};
enum class EnumItemsDirection
{
TopToBottom,
BottomToTop,
};
using TextState = HistoryView::TextState;
using CursorState = HistoryView::CursorState;
using PointState = HistoryView::PointState;
using StateRequest = HistoryView::StateRequest;
void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionUpdate(const QPoint &screenPos);
void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionCancel();
void updateSelected();
void performDrag();
int itemTop(not_null<const Element *> view) const;
void repaintItem(const Element *view);
void refreshItem(not_null<const Element *> view);
void resizeItem(not_null<Element *> view);
QPoint mapPointToItem(QPoint point, const Element *view) const;
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
void savePhotoToFile(not_null<PhotoData *> photo);
void saveDocumentToFile(not_null<DocumentData *> document);
void copyContextImage(not_null<PhotoData *> photo);
void showStickerPackInfo(not_null<DocumentData *> document);
void cancelContextDownload(not_null<DocumentData *> document);
void showContextInFolder(not_null<DocumentData *> document);
void openContextGif(FullMsgId itemId);
void copyContextText(FullMsgId itemId);
void copySelectedText();
TextForMimeData getSelectedText() const;
void updateVisibleTopItem();
void itemsAdded(Direction direction, int addedCount);
void updateSize();
void updateEmptyText();
void paintEmpty(Painter &p, not_null<const Ui::ChatStyle *> st);
void addEvents(Direction direction);
Element *viewForItem(const HistoryItem *item);
void toggleScrollDateShown();
void repaintScrollDateCallback();
bool displayScrollDate() const;
void scrollDateHide();
void scrollDateCheck();
void scrollDateHideByTimer();
// This function finds all history items that are displayed and calls template method
// for each found message (in given direction) in the passed history with passed top offset.
//
// Method has "bool (*Method)(not_null<Element*> view, int itemtop, int itembottom)" signature
// if it returns false the enumeration stops immidiately.
template<EnumItemsDirection direction, typename Method>
void enumerateItems(Method method);
// This function finds all userpics on the left that are displayed and calls template method
// for each found userpic (from the top to the bottom) using enumerateItems() method.
//
// Method has "bool (*Method)(not_null<Element*> view, int userpicTop)" signature
// if it returns false the enumeration stops immediately.
template<typename Method>
void enumerateUserpics(Method method);
// This function finds all date elements that are displayed and calls template method
// for each found date element (from the bottom to the top) using enumerateItems() method.
//
// Method has "bool (*Method)(not_null<HistoryItem*> item, int itemtop, int dateTop)" signature
// if it returns false the enumeration stops immediately.
template<typename Method>
void enumerateDates(Method method);
const not_null<Window::SessionController *> _controller;
const not_null<PeerData *> _peer;
const not_null<HistoryItem *> _item;
const not_null<History *> _history;
MTP::Sender _api;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
std::shared_ptr<Ui::ChatTheme> _theme;
std::vector<OwnedItem> _items;
std::set<uint64> _eventIds;
std::map<not_null<const HistoryItem *>, not_null<Element *>> _itemsByData;
base::flat_map<not_null<const HistoryItem *>, TimeId> _itemDates;
base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map<not_null<PeerData *>, Ui::PeerUserpicView> _userpics;
base::flat_map<not_null<PeerData *>, Ui::PeerUserpicView> _userpicsCache;
int _itemsTop = 0;
int _itemsWidth = 0;
int _itemsHeight = 0;
int _minHeight = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
Element *_visibleTopItem = nullptr;
int _visibleTopFromItem = 0;
bool _isChatWide = false;
bool _scrollDateShown = false;
Ui::Animations::Simple _scrollDateOpacity;
SingleQueuedInvokation _scrollDateCheck;
base::Timer _scrollDateHideTimer;
Element *_scrollDateLastItem = nullptr;
int _scrollDateLastItemTop = 0;
// Don't load anything until the memento was read.
bool _upLoaded = true;
bool _downLoaded = true;
bool _filterChanged = false;
Ui::Text::String _emptyText;
MouseAction _mouseAction = MouseAction::None;
TextSelectType _mouseSelectType = TextSelectType::Letters;
QPoint _dragStartPosition;
QPoint _mousePosition;
Element *_mouseActionItem = nullptr;
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;
Element *_selectedItem = nullptr;
TextSelection _selectedText;
bool _wasSelectedText = false; // was some text selected in current drag action
Qt::CursorShape _cursor = style::cur_default;
base::unique_qptr<Ui::PopupMenu> _menu;
QPoint _trippleClickPoint;
base::Timer _trippleClickTimer;
rpl::event_stream<int> _scrollToSignal;
};
} // namespace EditedLog

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ayu/database/entities.h"
class History;
namespace HistoryView
{
class ElementDelegate;
class Element;
} // namespace HistoryView
namespace EditedLog
{
class OwnedItem;
void GenerateItems(
not_null<HistoryView::ElementDelegate *> delegate,
not_null<History *> history,
EditedMessage message,
Fn<void(OwnedItem item, TimeId sentDate, MsgId)> callback);
// Smart pointer wrapper for HistoryItem* that destroys the owned item.
class OwnedItem
{
public:
OwnedItem(std::nullptr_t = nullptr);
OwnedItem(
not_null<HistoryView::ElementDelegate *> delegate,
not_null<HistoryItem *> data);
OwnedItem(const OwnedItem &other) = delete;
OwnedItem &operator=(const OwnedItem &other) = delete;
OwnedItem(OwnedItem &&other);
OwnedItem &operator=(OwnedItem &&other);
~OwnedItem();
[[nodiscard]] HistoryView::Element *get() const
{
return _view.get();
}
[[nodiscard]] HistoryView::Element *operator->() const
{
return get();
}
[[nodiscard]] operator HistoryView::Element *() const
{
return get();
}
void refreshView(not_null<HistoryView::ElementDelegate *> delegate);
void clearView();
private:
HistoryItem *_data = nullptr;
std::unique_ptr<HistoryView::Element> _view;
};
} // namespace EditedLog

View file

@ -0,0 +1,370 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ayu/ui/sections/edited/edited_log_section.h"
#include "ayu/ui/sections/edited/edited_log_inner.h"
#include "profile/profile_back_button.h"
#include "core/shortcuts.h"
#include "ui/effects/animations.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/ui_utility.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "window/themes/window_theme.h"
#include "window/window_adaptive.h"
#include "window/window_session_controller.h"
#include "ui/boxes/confirm_box.h"
#include "base/timer.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_window.h"
#include "styles/style_info.h"
namespace EditedLog
{
class FixedBar final : public TWidget
{
public:
FixedBar(
QWidget *parent,
not_null<Window::SessionController *> controller,
not_null<PeerData *> peer);
// When animating mode is enabled the content is hidden and the
// whole fixed bar acts like a back button.
void setAnimatingMode(bool enabled);
void goBack();
protected:
void mousePressEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
not_null<Window::SessionController *> _controller;
not_null<PeerData *> _peer;
object_ptr<Profile::BackButton> _backButton;
object_ptr<Ui::CrossButton> _cancel;
bool _animatingMode = false;
};
object_ptr<Window::SectionWidget> SectionMemento::createWidget(
QWidget *parent,
not_null<Window::SessionController *> controller,
Window::Column column,
const QRect &geometry)
{
if (column == Window::Column::Third) {
return nullptr;
}
auto result = object_ptr<Widget>(parent, controller, _peer, _item);
result->setInternalState(geometry, this);
return result;
}
FixedBar::FixedBar(
QWidget *parent,
not_null<Window::SessionController *> controller,
not_null<PeerData *> peer)
: TWidget(parent), _controller(controller), _peer(peer), _backButton(
this,
&controller->session(),
tr::lng_admin_log_title_all(tr::now),
controller->adaptive().oneColumnValue()), _cancel(this, st::historyAdminLogCancelSearch)
{
_backButton->moveToLeft(0, 0);
_backButton->setClickedCallback([=]
{ goBack(); });
_cancel->hide(anim::type::instant);
}
void FixedBar::goBack()
{
_controller->showBackFromStack();
}
int FixedBar::resizeGetHeight(int newWidth)
{
auto filterLeft = newWidth;
auto cancelLeft = filterLeft - _cancel->width();
_cancel->moveToLeft(cancelLeft, 0);
auto searchShownLeft = st::topBarArrowPadding.left();
auto searchHiddenLeft = filterLeft - 0;
auto searchCurrentLeft = anim::interpolate(searchHiddenLeft, searchShownLeft, 0.0);
_backButton->resizeToWidth(searchCurrentLeft);
_backButton->moveToLeft(0, 0);
auto newHeight = _backButton->height();
return newHeight;
}
void FixedBar::setAnimatingMode(bool enabled)
{
if (_animatingMode != enabled) {
_animatingMode = enabled;
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
if (_animatingMode) {
setAttribute(Qt::WA_OpaquePaintEvent, false);
hideChildren();
}
else {
setAttribute(Qt::WA_OpaquePaintEvent);
showChildren();
_cancel->setVisible(false);
}
show();
}
}
void FixedBar::paintEvent(QPaintEvent *e)
{
if (!_animatingMode) {
auto p = QPainter(this);
p.fillRect(e->rect(), st::topBarBg);
}
}
void FixedBar::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) {
goBack();
}
else {
TWidget::mousePressEvent(e);
}
}
Widget::Widget(
QWidget *parent,
not_null<Window::SessionController *> controller,
not_null<PeerData *> peer,
not_null<HistoryItem *> item)
: Window::SectionWidget(parent, controller, rpl::single<PeerData *>(peer)),
_scroll(this, st::historyScroll, false),
_fixedBar(this, controller, peer),
_fixedBarShadow(this),
_whatIsThis(
this,
tr::lng_admin_log_about(tr::now),
st::historyComposeButton),
_item(item)
{
_fixedBar->move(0, 0);
_fixedBar->resizeToWidth(width());
_fixedBar->show();
_fixedBarShadow->raise();
controller->adaptive().value(
) | rpl::start_with_next([=]
{
updateAdaptiveLayout();
}, lifetime());
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, controller, peer, item));
_inner->scrollToSignal(
) | rpl::start_with_next([=](int top)
{
_scroll->scrollToY(top);
}, lifetime());
_scroll->move(0, _fixedBar->height());
_scroll->show();
_scroll->scrolls(
) | rpl::start_with_next([=]
{
onScroll();
}, lifetime());
_whatIsThis->setClickedCallback([=]
{
controller->show(Ui::MakeInformBox(peer->isMegagroup()
? tr::lng_admin_log_about_text()
: tr::lng_admin_log_about_text_channel()));
});
setupShortcuts();
}
void Widget::updateAdaptiveLayout()
{
_fixedBarShadow->moveToLeft(
controller()->adaptive().isOneColumn()
? 0
: st::lineWidth,
_fixedBar->height());
}
not_null<PeerData *> Widget::channel() const
{
return _inner->channel();
}
Dialogs::RowDescriptor Widget::activeChat() const
{
return {
channel()->owner().history(channel()),
FullMsgId(channel()->id, ShowAtUnreadMsgId)
};
}
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams &params)
{
if (params.withTopBarShadow) _fixedBarShadow->hide();
auto result = Ui::GrabWidget(this);
if (params.withTopBarShadow) _fixedBarShadow->show();
return result;
}
void Widget::doSetInnerFocus()
{
_inner->setFocus();
}
bool Widget::showInternal(
not_null<Window::SectionMemento *> memento,
const Window::SectionShow &params)
{
if (auto logMemento = dynamic_cast<SectionMemento *>(memento.get())) {
if (logMemento->getPeer() == channel()) {
restoreState(logMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento *> memento)
{
setGeometry(geometry);
Ui::SendPendingMoveResizeEvents(this);
restoreState(memento);
}
void Widget::setupShortcuts()
{
// todo: smth
}
std::shared_ptr<Window::SectionMemento> Widget::createMemento()
{
auto result = std::make_shared<SectionMemento>(channel(), _item);
saveState(result.get());
return result;
}
void Widget::saveState(not_null<SectionMemento *> memento)
{
memento->setScrollTop(_scroll->scrollTop());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<SectionMemento *> memento)
{
_inner->restoreState(memento);
auto scrollTop = memento->getScrollTop();
_scroll->scrollToY(scrollTop);
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void Widget::resizeEvent(QResizeEvent *e)
{
if (!width() || !height()) {
return;
}
auto contentWidth = width();
auto newScrollTop = _scroll->scrollTop() + topDelta();
_fixedBar->resizeToWidth(contentWidth);
_fixedBarShadow->resize(contentWidth, st::lineWidth);
auto bottom = height();
auto scrollHeight = bottom - _fixedBar->height() - _whatIsThis->height();
auto scrollSize = QSize(contentWidth, scrollHeight);
if (_scroll->size() != scrollSize) {
_scroll->resize(scrollSize);
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
_inner->restoreScrollPosition();
}
if (!_scroll->isHidden()) {
if (topDelta()) {
_scroll->scrollToY(newScrollTop);
}
auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
auto fullWidthButtonRect = myrtlrect(0, bottom - _whatIsThis->height(), contentWidth, _whatIsThis->height());
_whatIsThis->setGeometry(fullWidthButtonRect);
}
void Widget::paintEvent(QPaintEvent *e)
{
if (animatingShow()) {
SectionWidget::paintEvent(e);
return;
}
else if (controller()->contentOverlapped(this, e)) {
return;
}
//if (hasPendingResizedItems()) {
// updateListSize();
//}
//auto ms = crl::now();
//_historyDownShown.step(ms);
const auto clip = e->rect();
SectionWidget::PaintBackground(controller(), _inner->theme(), this, clip);
}
void Widget::onScroll()
{
int scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void Widget::showAnimatedHook(
const Window::SectionSlideParams &params)
{
_fixedBar->setAnimatingMode(true);
if (params.withTopBarShadow) _fixedBarShadow->show();
}
void Widget::showFinishedHook()
{
_fixedBar->setAnimatingMode(false);
}
bool Widget::floatPlayerHandleWheelEvent(QEvent *e)
{
return _scroll->viewportEvent(e);
}
QRect Widget::floatPlayerAvailableRect()
{
return mapToGlobal(_scroll->geometry());
}
} // namespace EditedLog

View file

@ -0,0 +1,159 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "window/section_widget.h"
#include "window/section_memento.h"
#include "ayu/ui/sections/edited/edited_log_item.h"
#include "mtproto/sender.h"
namespace Ui
{
class ScrollArea;
class PlainShadow;
class FlatButton;
} // namespace Ui
namespace Profile
{
class BackButton;
} // namespace Profile
namespace EditedLog
{
class FixedBar;
class InnerWidget;
class SectionMemento;
class Widget final : public Window::SectionWidget
{
public:
Widget(
QWidget *parent,
not_null<Window::SessionController *> controller,
not_null<PeerData *> peer,
not_null<HistoryItem *> item);
not_null<PeerData *> channel() const;
Dialogs::RowDescriptor activeChat() const override;
bool hasTopBarShadow() const override
{
return true;
}
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento *> memento,
const Window::SectionShow &params) override;
std::shared_ptr<Window::SectionMemento> createMemento() override;
void setInternalState(const QRect &geometry, not_null<SectionMemento *> memento);
// Float player interface.
bool floatPlayerHandleWheelEvent(QEvent *e) override;
QRect floatPlayerAvailableRect() override;
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void showAnimatedHook(
const Window::SectionSlideParams &params) override;
void showFinishedHook() override;
void doSetInnerFocus() override;
private:
void onScroll();
void updateAdaptiveLayout();
void saveState(not_null<SectionMemento *> memento);
void restoreState(not_null<SectionMemento *> memento);
void setupShortcuts();
object_ptr<Ui::ScrollArea> _scroll;
QPointer<InnerWidget> _inner;
object_ptr<FixedBar> _fixedBar;
object_ptr<Ui::PlainShadow> _fixedBarShadow;
object_ptr<Ui::FlatButton> _whatIsThis;
not_null<HistoryItem *> _item;
};
class SectionMemento : public Window::SectionMemento
{
public:
using Element = HistoryView::Element;
SectionMemento(not_null<PeerData *> peer, not_null<HistoryItem *> item)
: _peer(peer),
_item(item)
{
}
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::SessionController *> controller,
Window::Column column,
const QRect &geometry) override;
not_null<PeerData *> getPeer() const
{
return _peer;
}
void setScrollTop(int scrollTop)
{
_scrollTop = scrollTop;
}
int getScrollTop() const
{
return _scrollTop;
}
void setItems(
std::vector<OwnedItem> &&items,
std::set<uint64> &&eventIds,
bool upLoaded,
bool downLoaded)
{
_items = std::move(items);
_eventIds = std::move(eventIds);
_upLoaded = upLoaded;
_downLoaded = downLoaded;
}
std::vector<OwnedItem> takeItems()
{
return std::move(_items);
}
std::set<uint64> takeEventIds()
{
return std::move(_eventIds);
}
bool upLoaded() const
{
return _upLoaded;
}
bool downLoaded() const
{
return _downLoaded;
}
private:
not_null<PeerData *> _peer;
not_null<HistoryItem *> _item;
int _scrollTop = 0;
std::vector<not_null<UserData *>> _admins;
std::vector<not_null<UserData *>> _adminsCanEdit;
std::vector<OwnedItem> _items;
std::set<uint64> _eventIds;
bool _upLoaded = false;
bool _downLoaded = true;
};
} // namespace EditedLog

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#include "icon_picker.h" #include "icon_picker.h"
#include "ayu/ayu_settings.h" #include "ayu/ayu_settings.h"
#include "core/application.h" #include "core/application.h"
@ -23,8 +22,7 @@
void drawIcon(QPainter &p, const QImage &icon, int offset, bool selected) void drawIcon(QPainter &p, const QImage &icon, int offset, bool selected)
{ {
if (selected) if (selected) {
{
p.save(); p.save();
p.setPen(QPen(st::iconPreviewStroke, 2)); p.setPen(QPen(st::iconPreviewStroke, 2));
p.drawEllipse(offset + 2, 2, 68, 68); p.drawEllipse(offset + 2, 2, 68, 68);
@ -35,7 +33,8 @@ void drawIcon(QPainter& p, const QImage& icon, int offset, bool selected)
p.drawImage(rect, icon); p.drawImage(rect, icon);
} }
IconPicker::IconPicker(QWidget* parent) : RpWidget(parent) IconPicker::IconPicker(QWidget *parent)
: RpWidget(parent)
{ {
setMinimumSize(st::boxWidth, 72); setMinimumSize(st::boxWidth, 72);
} }
@ -61,24 +60,20 @@ void IconPicker::mousePressEvent(QMouseEvent* e)
auto changed = false; auto changed = false;
auto x = e->pos().x(); auto x = e->pos().x();
if (x <= 64 && settings->appIcon != AyuSettings::DEFAULT_ICON) if (x <= 64 && settings->appIcon != AyuSettings::DEFAULT_ICON) {
{
settings->set_appIcon(AyuSettings::DEFAULT_ICON); settings->set_appIcon(AyuSettings::DEFAULT_ICON);
changed = true; changed = true;
} }
else if (x >= 64 + 16 && x <= 64 + 16 + 64 && settings->appIcon != AyuSettings::ALT_ICON) else if (x >= 64 + 16 && x <= 64 + 16 + 64 && settings->appIcon != AyuSettings::ALT_ICON) {
{
settings->set_appIcon(AyuSettings::ALT_ICON); settings->set_appIcon(AyuSettings::ALT_ICON);
changed = true; changed = true;
} }
else if (x >= 64 + 16 + 64 + 16 && x < 64 + 16 + 64 + 16 + 64 && settings->appIcon != AyuSettings::NOTHING_ICON) else if (x >= 64 + 16 + 64 + 16 && x < 64 + 16 + 64 + 16 + 64 && settings->appIcon != AyuSettings::NOTHING_ICON) {
{
settings->set_appIcon(AyuSettings::NOTHING_ICON); settings->set_appIcon(AyuSettings::NOTHING_ICON);
changed = true; changed = true;
} }
if (changed) if (changed) {
{
AyuSettings::save(); AyuSettings::save();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"

View file

@ -54,13 +54,15 @@ class PainterHighQualityEnabler;
const char kStreamerMode[] = const char kStreamerMode[] =
"streamer-mode"; "streamer-mode";
base::options::toggle StreamerMode({ base::options::toggle StreamerMode(
{
.id = kStreamerMode, .id = kStreamerMode,
.name = "Show streamer mode toggles", .name = "Show streamer mode toggles",
.description = "Streamer mode completely hides AyuGram windows and notifications from capture apps.", .description = "Streamer mode completely hides AyuGram windows and notifications from capture apps.",
.scope = base::options::windows, .scope = base::options::windows,
.restartRequired = true .restartRequired = true
}); }
);
not_null<Ui::RpWidget *> AddInnerToggle( not_null<Ui::RpWidget *> AddInnerToggle(
not_null<Ui::VerticalLayout *> container, not_null<Ui::VerticalLayout *> container,
@ -75,8 +77,7 @@ not_null<Ui::RpWidget*> AddInnerToggle(
container, container,
nullptr, nullptr,
st)); st));
if (icon) if (icon) {
{
AddButtonIcon(button, st, std::move(icon)); AddButtonIcon(button, st, std::move(icon));
} }
@ -99,16 +100,17 @@ not_null<Ui::RpWidget*> AddInnerToggle(
}; };
const auto state = button->lifetime().make_state<State>( const auto state = button->lifetime().make_state<State>(
st.toggle, st.toggle,
[=] { toggleButton->update(); }); [=]
{ toggleButton->update(); });
state->innerChecks = std::move(innerCheckViews); state->innerChecks = std::move(innerCheckViews);
const auto countChecked = [=] const auto countChecked = [=]
{ {
return ranges::count_if( return ranges::count_if(
state->innerChecks, state->innerChecks,
[](const auto& v) { return v->checked(); }); [](const auto &v)
{ return v->checked(); });
}; };
for (const auto& innerCheck : state->innerChecks) for (const auto &innerCheck : state->innerChecks) {
{
innerCheck->checkedChanges( innerCheck->checkedChanges(
) | rpl::to_empty | start_to_stream( ) | rpl::to_empty | start_to_stream(
state->anyChanges, state->anyChanges,
@ -162,7 +164,8 @@ not_null<Ui::RpWidget*> AddInnerToggle(
rpl::empty_value() rpl::empty_value()
) | rpl::map(countChecked) | start_with_next([=](int count) ) | rpl::map(countChecked) | start_with_next([=](int count)
{ {
checkView->setChecked(count == GhostModeOptionsCount, anim::type::normal); checkView->setChecked(count == GhostModeOptionsCount,
anim::type::normal);
}, toggleButton->lifetime()); }, toggleButton->lifetime());
checkView->setLocked(locked.has_value()); checkView->setLocked(locked.has_value());
checkView->finishAnimating(); checkView->finishAnimating();
@ -198,8 +201,7 @@ not_null<Ui::RpWidget*> AddInnerToggle(
const auto progress = state->animation.value( const auto progress = state->animation.value(
wrap->toggled() ? 1. : 0.); wrap->toggled() ? 1. : 0.);
auto hq = std::optional<PainterHighQualityEnabler>(); auto hq = std::optional<PainterHighQualityEnabler>();
if (progress > 0.) if (progress > 0.) {
{
hq.emplace(p); hq.emplace(p);
p.translate(center); p.translate(center);
p.rotate(progress * 180.); p.rotate(progress * 180.);
@ -228,7 +230,8 @@ not_null<Ui::RpWidget*> AddInnerToggle(
) | rpl::skip(1) | start_with_next([=](bool toggled) ) | rpl::skip(1) | start_with_next([=](bool toggled)
{ {
state->animation.start( state->animation.start(
[=] { arrow->update(); }, [=]
{ arrow->update(); },
toggled ? 0. : 1., toggled ? 0. : 1.,
toggled ? 1. : 0., toggled ? 1. : 0.,
st::slideWrapDuration); st::slideWrapDuration);
@ -236,8 +239,7 @@ not_null<Ui::RpWidget*> AddInnerToggle(
const auto handleLocked = [=] const auto handleLocked = [=]
{ {
if (locked.has_value()) if (locked.has_value()) {
{
Ui::Toast::Show(container, *locked); Ui::Toast::Show(container, *locked);
return true; return true;
} }
@ -247,8 +249,7 @@ not_null<Ui::RpWidget*> AddInnerToggle(
button->clicks( button->clicks(
) | start_with_next([=] ) | start_with_next([=]
{ {
if (!handleLocked()) if (!handleLocked()) {
{
wrap->toggle(!wrap->toggled(), anim::type::normal); wrap->toggle(!wrap->toggled(), anim::type::normal);
} }
}, button->lifetime()); }, button->lifetime());
@ -256,11 +257,9 @@ not_null<Ui::RpWidget*> AddInnerToggle(
toggleButton->clicks( toggleButton->clicks(
) | start_with_next([=] ) | start_with_next([=]
{ {
if (!handleLocked()) if (!handleLocked()) {
{
const auto checked = !checkView->checked(); const auto checked = !checkView->checked();
for (const auto& innerCheck : state->innerChecks) for (const auto &innerCheck : state->innerChecks) {
{
innerCheck->setChecked(checked, anim::type::normal); innerCheck->setChecked(checked, anim::type::normal);
} }
} }
@ -271,6 +270,7 @@ not_null<Ui::RpWidget*> AddInnerToggle(
namespace Settings namespace Settings
{ {
rpl::producer<QString> Ayu::title() rpl::producer<QString> Ayu::title()
{ {
return tr::ayu_AyuPreferences(); return tr::ayu_AyuPreferences();
@ -310,13 +310,13 @@ namespace Settings
const auto restarter = (option.relevant() && option.restartRequired()) const auto restarter = (option.relevant() && option.restartRequired())
? button->lifetime().make_state<base::Timer>() ? button->lifetime().make_state<base::Timer>()
: nullptr; : nullptr;
if (restarter) if (restarter) {
{
restarter->setCallback([=] restarter->setCallback([=]
{ {
window->show(Ui::MakeConfirmBox({ window->show(Ui::MakeConfirmBox({
.text = tr::lng_settings_need_restart(), .text = tr::lng_settings_need_restart(),
.confirmed = [] { Core::Restart(); }, .confirmed = []
{ Core::Restart(); },
.confirmText = tr::lng_settings_restart_now(), .confirmText = tr::lng_settings_restart_now(),
.cancelText = tr::lng_settings_restart_later(), .cancelText = tr::lng_settings_restart_later(),
})); }));
@ -325,23 +325,20 @@ namespace Settings
button->toggledChanges( button->toggledChanges(
) | start_with_next([=, &option](bool toggled) ) | start_with_next([=, &option](bool toggled)
{ {
if (!option.relevant() && toggled != option.defaultValue()) if (!option.relevant() && toggled != option.defaultValue()) {
{
toggles->fire_copy(option.defaultValue()); toggles->fire_copy(option.defaultValue());
window->showToast( window->showToast(
tr::lng_settings_experimental_irrelevant(tr::now)); tr::lng_settings_experimental_irrelevant(tr::now));
return; return;
} }
option.set(toggled); option.set(toggled);
if (restarter) if (restarter) {
{
restarter->callOnce(st::settingsButtonNoIcon.toggle.duration); restarter->callOnce(st::settingsButtonNoIcon.toggle.duration);
} }
}, container->lifetime()); }, container->lifetime());
const auto &description = option.description(); const auto &description = option.description();
if (!description.isEmpty()) if (!description.isEmpty()) {
{
AddSkip(container); AddSkip(container);
AddDividerText(container, rpl::single(description)); AddDividerText(container, rpl::single(description));
} }
@ -456,8 +453,7 @@ namespace Settings
object_ptr<Ui::VerticalLayout>(container)); object_ptr<Ui::VerticalLayout>(container));
const auto verticalLayout = wrap->entity(); const auto verticalLayout = wrap->entity();
auto innerChecks = std::vector<not_null<Ui::AbstractCheckView *>>(); auto innerChecks = std::vector<not_null<Ui::AbstractCheckView *>>();
for (const auto& entry : checkboxes) for (const auto &entry : checkboxes) {
{
const auto c = addCheckbox(verticalLayout, entry.checkboxLabel, entry.initial); const auto c = addCheckbox(verticalLayout, entry.checkboxLabel, entry.initial);
c->checkedValue( c->checkedValue(
) | start_with_next([=](bool enabled) ) | start_with_next([=](bool enabled)
@ -791,9 +787,11 @@ namespace Settings
slider->setPseudoDiscrete( slider->setPseudoDiscrete(
100 + 1, // thx tg 100 + 1, // thx tg
[=](int amount) { return amount; }, [=](int amount)
{ return amount; },
settings->recentStickersCount, settings->recentStickersCount,
[=](int amount) { updateLabel(amount); }, [=](int amount)
{ updateLabel(amount); },
[=](int amount) [=](int amount)
{ {
updateLabel(amount); updateLabel(amount);
@ -909,8 +907,7 @@ namespace Settings
AddDividerText(container, tr::ayu_SettingsCustomizationHint()); AddDividerText(container, tr::ayu_SettingsCustomizationHint());
// todo: compilation flag // todo: compilation flag
if constexpr (false) if constexpr (false) {
{
AddSkip(container); AddSkip(container);
SetupAyuSync(container); SetupAyuSync(container);
AddSkip(container); AddSkip(container);
@ -937,4 +934,5 @@ namespace Settings
ResizeFitChild(this, content); ResizeFitChild(this, content);
} }
} // namespace Settings } // namespace Settings

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "base/options.h" #include "base/options.h"
@ -15,15 +14,16 @@ class BoxContent;
namespace Window namespace Window
{ {
class Controller; class Controller;
class SessionController; class SessionController;
} // namespace Window } // namespace Window
extern const char kStreamerMode[]; extern const char kStreamerMode[];
extern base::options::toggle StreamerMode; extern base::options::toggle StreamerMode;
namespace Settings namespace Settings
{ {
class Ayu : public Section<Ayu> class Ayu : public Section<Ayu>
{ {
public: public:
@ -64,4 +64,5 @@ namespace Settings
void setupContent(not_null<Window::SessionController *> controller); void setupContent(not_null<Window::SessionController *> controller);
}; };
} // namespace Settings } // namespace Settings

View file

@ -13,7 +13,6 @@
constexpr auto kMaxChannelId = -1000000000000; constexpr auto kMaxChannelId = -1000000000000;
QString IDString(not_null<PeerData *> peer) QString IDString(not_null<PeerData *> peer)
{ {
auto resultId = QString::number(peerIsUser(peer->id) auto resultId = QString::number(peerIsUser(peer->id)
@ -25,14 +24,11 @@ QString IDString(not_null<PeerData*> peer)
: peer->id.value); : peer->id.value);
const auto settings = &AyuSettings::getInstance(); const auto settings = &AyuSettings::getInstance();
if (settings->showPeerId == 2) if (settings->showPeerId == 2) {
{ if (peer->isChannel()) {
if (peer->isChannel())
{
resultId = QString::number(peerToChannel(peer->id).bare - kMaxChannelId).prepend("-"); resultId = QString::number(peerToChannel(peer->id).bare - kMaxChannelId).prepend("-");
} }
else if (peer->isChat()) else if (peer->isChat()) {
{
resultId = resultId.prepend("-"); resultId = resultId.prepend("-");
} }
} }
@ -47,7 +43,6 @@ QString IDString(MsgId topic_root_id)
return resultId; return resultId;
} }
rpl::producer<TextWithEntities> IDValue(not_null<PeerData *> peer) rpl::producer<TextWithEntities> IDValue(not_null<PeerData *> peer)
{ {
return rpl::single(IDString(peer)) | Ui::Text::ToWithEntities(); return rpl::single(IDString(peer)) | Ui::Text::ToWithEntities();

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once

View file

@ -0,0 +1,86 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "ayu_mapper.h"
#include "history/history_item.h"
#include "history/history_item_components.h"
#include "history/history.h"
namespace AyuMapper
{
int mapItemFlagsToMTPFlags(not_null<HistoryItem *> item) {
int flags = 0;
const auto thread = item->topic()
? (Data::Thread*)item->topic()
: item->history();
const auto unseen = item->unread(thread);
if (unseen) {
flags |= 1;
}
if (item->out()) {
flags |= 2;
}
if (const auto reply = item->Get<HistoryMessageForwarded>()) {
flags |= 4;
}
if (const auto reply = item->Get<HistoryMessageReply>()) {
flags |= 8;
}
if (item->mentionsMe()) {
flags |= 16;
}
if (item->isUnreadMedia()) {
flags |= 32;
}
if (item->isSilent()) {
flags |= 8192;
}
if (item->isPost()) {
flags |= 16384;
}
if (item->isScheduled()) {
flags |= 262144;
}
// todo: legacy
// if (item->isLegacy()) {
// flags |= 524288;
// }
if (item->hideEditedBadge()) {
flags |= 2097152;
}
if (item->isPinned()) {
flags |= 16777216;
}
if (item->forbidsForward()) {
flags |= 67108864;
}
if (item->topic() && item->topicRootId() == item->id) {
flags |= 134217728;
}
return flags;
}
}

View file

@ -0,0 +1,14 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
namespace AyuMapper
{
int mapItemFlagsToMTPFlags(not_null<HistoryItem *> item);
} // namespace AyuMapper

View file

@ -22,12 +22,9 @@
Main::Session *getSession(ID userId) Main::Session *getSession(ID userId)
{ {
for (const auto& [index, account] : Core::App().domain().accounts()) for (const auto &[index, account] : Core::App().domain().accounts()) {
{ if (const auto session = account->maybeSession()) {
if (const auto session = account->maybeSession()) if (session->userId().bare == userId) {
{
if (session->userId().bare == userId)
{
return session; return session;
} }
} }
@ -56,14 +53,12 @@ void dispatchToMainThread(std::function<void()> callback)
not_null<History *> getHistoryFromDialogId(ID dialogId, Main::Session *session) not_null<History *> getHistoryFromDialogId(ID dialogId, Main::Session *session)
{ {
if (dialogId > 0) if (dialogId > 0) {
{
return session->data().history(peerFromUser(dialogId)); return session->data().history(peerFromUser(dialogId));
} }
auto history = session->data().history(peerFromChannel(abs(dialogId))); auto history = session->data().history(peerFromChannel(abs(dialogId)));
if (history->folderKnown()) if (history->folderKnown()) {
{
return history; return history;
} }
@ -80,8 +75,7 @@ ID getDialogIdFromPeer(not_null<PeerData*> peer)
? peerToChannel(peer->id).bare ? peerToChannel(peer->id).bare
: peer->id.value; : peer->id.value;
if (peer->isChannel() || peer->isChat()) if (peer->isChannel() || peer->isChat()) {
{
peerId = -peerId; peerId = -peerId;
} }
@ -90,8 +84,7 @@ ID getDialogIdFromPeer(not_null<PeerData*> peer)
std::pair<std::string, std::string> serializeTextWithEntities(not_null<HistoryItem *> item) std::pair<std::string, std::string> serializeTextWithEntities(not_null<HistoryItem *> item)
{ {
if (item->emptyText()) if (item->emptyText()) {
{
return std::make_pair("", ""); return std::make_pair("", "");
} }
@ -100,14 +93,12 @@ std::pair<std::string, std::string> serializeTextWithEntities(not_null<HistoryIt
auto entities = EntitiesToMTP(&item->history()->owner().session(), textWithEntities.entities, auto entities = EntitiesToMTP(&item->history()->owner().session(), textWithEntities.entities,
Api::ConvertOption::SkipLocal); Api::ConvertOption::SkipLocal);
if (entities.v.isEmpty()) if (entities.v.isEmpty()) {
{
return std::make_pair(text, ""); return std::make_pair(text, "");
} }
auto buff = mtpBuffer(); auto buff = mtpBuffer();
for (auto entity : entities.v) for (auto entity : entities.v) {
{
entity.write(buff); entity.write(buff);
} }

View file

@ -4,7 +4,6 @@
// but be respectful and credit the original author. // but be respectful and credit the original author.
// //
// Copyright @Radolyn, 2023 // Copyright @Radolyn, 2023
#pragma once #pragma once
#include "ayu/sync/models.h" #include "ayu/sync/models.h"

View file

@ -10,7 +10,8 @@
#include <ShlObj_core.h> #include <ShlObj_core.h>
void reloadAppIconFromTaskBar() { void reloadAppIconFromTaskBar()
{
QString appdata = QDir::fromNativeSeparators(qgetenv("APPDATA")); QString appdata = QDir::fromNativeSeparators(qgetenv("APPDATA"));
QString ayugramIconPath = appdata + "/AyuGram.ico"; QString ayugramIconPath = appdata + "/AyuGram.ico";

View file

@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ayu/database/ayu_database.h" #include "ayu/database/ayu_database.h"
#include "ayu/messages/ayu_messages_controller.h" #include "ayu/messages/ayu_messages_controller.h"
#include "ayu/ui/boxes/message_history_box.h" #include "ayu/ui/boxes/message_history_box.h"
#include "ayu/ui/sections/edited/edited_log_section.h"
namespace HistoryView { namespace HistoryView {
namespace { namespace {
@ -1091,8 +1092,7 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
{ {
result->addAction(tr::ayu_EditsHistoryMenuText(tr::now), [=] result->addAction(tr::ayu_EditsHistoryMenuText(tr::now), [=]
{ {
auto box = Box<AyuUi::MessageHistoryBox>(item); item->history()->session().tryResolveWindow()->showSection(std::make_shared<EditedLog::SectionMemento>(item->history()->peer, item));
Ui::show(std::move(box));
}, &st::menuIconInfo); }, &st::menuIconInfo);
} }