upload: import Ayu initial

Co-authored-by: SharapaGorg <sharapov.savely@yandex.ru>
This commit is contained in:
ZavaruKitsu 2023-06-03 19:00:05 +03:00
parent be89e57d27
commit eaba9781a5
86 changed files with 282538 additions and 140 deletions

BIN
.github/AyuGram.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -1,81 +1,37 @@
# [Telegram Desktop][telegram_desktop] Official Messenger # AyuGram
This is the complete source code and the build instructions for the official [Telegram][telegram] messenger desktop client, based on the [Telegram API][telegram_api] and the [MTProto][telegram_proto] secure protocol. ![AyuGram Logo](.github/AyuGram.png)
[![Version](https://badge.fury.io/gh/telegramdesktop%2Ftdesktop.svg)](https://github.com/telegramdesktop/tdesktop/releases) ## Features
[![Build Status](https://github.com/telegramdesktop/tdesktop/workflows/Windows./badge.svg)](https://github.com/telegramdesktop/tdesktop/actions)
[![Build Status](https://github.com/telegramdesktop/tdesktop/workflows/MacOS./badge.svg)](https://github.com/telegramdesktop/tdesktop/actions)
[![Build Status](https://github.com/telegramdesktop/tdesktop/workflows/Linux./badge.svg)](https://github.com/telegramdesktop/tdesktop/actions)
[![Preview of Telegram Desktop][preview_image]][preview_image_url] - Disable read packets sending
- Disable online packets sending
- Disable typing & upload packets sending
- Auto offline
- Messages history (+ deleted ones)
- Using scheduled messages to keep offline
The source code is published under GPLv3 with OpenSSL exception, the license is available [here][license]. Technically, we have the **Ghost mode** starter pack.
## Supported systems Also, we have a cool **purple icon**.
The latest version is available for ## Downloads? / FAQ?
* [Windows 7 and above (64 bit)](https://telegram.org/dl/desktop/win64) ([portable](https://telegram.org/dl/desktop/win64_portable)) We have both **Windows** and **Linux** builds.
* [Windows 7 and above (32 bit)](https://telegram.org/dl/desktop/win) ([portable](https://telegram.org/dl/desktop/win_portable))
* [macOS 10.12 and above](https://telegram.org/dl/desktop/mac)
* [Linux static build for 64 bit](https://telegram.org/dl/desktop/linux)
* [Snap](https://snapcraft.io/telegram-desktop)
* [Flatpak](https://flathub.org/apps/details/org.telegram.desktop)
## Old system versions Follow our [Telegram channel](https://t.me/ayugram1338). FAQ can be found here.
Version **2.4.4** was the last that supports older systems ## May I get banned?
* [OS X 10.10 and 10.11](https://updates.tdesktop.com/tosx/tsetup-osx.2.4.4.dmg) Well, *you* **can't**, because you're just an ordinary user.
* [Linux static build for 32 bit](https://updates.tdesktop.com/tlinux32/tsetup32.2.4.4.tar.xz)
Version **1.8.15** was the last that supports older systems ## How to build
* [Windows XP and Vista](https://updates.tdesktop.com/tsetup/tsetup.1.8.15.exe) ([portable](https://updates.tdesktop.com/tsetup/tportable.1.8.15.zip)) Follow [official guide](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-win-x64.md).
* [OS X 10.8 and 10.9](https://updates.tdesktop.com/tmac/tsetup.1.8.15.dmg)
* [OS X 10.6 and 10.7](https://updates.tdesktop.com/tmac32/tsetup32.1.8.15.dmg)
## Third-party ### Remarks
* Qt 6 ([LGPL](http://doc.qt.io/qt-6/lgpl.html)) and Qt 5.15 ([LGPL](http://doc.qt.io/qt-5/lgpl.html)) slightly patched Make sure you have these components installed with VS Build Tools:
* OpenSSL 1.1.1 and 1.0.1 ([OpenSSL License](https://www.openssl.org/source/license.html)) - C++ MFC latest (x86 & x64)
* WebRTC ([New BSD License](https://github.com/desktop-app/tg_owt/blob/master/LICENSE)) - C++ ATL latest (x86 & x64)
* zlib 1.2.11 ([zlib License](http://www.zlib.net/zlib_license.html)) - latest Windows 11 SDK
* LZMA SDK 9.20 ([public domain](http://www.7-zip.org/sdk.html))
* liblzma ([public domain](http://tukaani.org/xz/))
* Google Breakpad ([License](https://chromium.googlesource.com/breakpad/breakpad/+/master/LICENSE))
* Google Crashpad ([Apache License 2.0](https://chromium.googlesource.com/crashpad/crashpad/+/master/LICENSE))
* GYP ([BSD License](https://github.com/bnoordhuis/gyp/blob/master/LICENSE))
* Ninja ([Apache License 2.0](https://github.com/ninja-build/ninja/blob/master/COPYING))
* OpenAL Soft ([LGPL](https://github.com/kcat/openal-soft/blob/master/COPYING))
* Opus codec ([BSD License](http://www.opus-codec.org/license/))
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
* Guideline Support Library ([MIT License](https://github.com/Microsoft/GSL/blob/master/LICENSE))
* Range-v3 ([Boost License](https://github.com/ericniebler/range-v3/blob/master/LICENSE.txt))
* Open Sans font ([Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html))
* Vazir font ([SIL Open Font License 1.1](https://github.com/rastikerdar/vazir-font/blob/master/OFL.txt))
* Emoji alpha codes ([MIT License](https://github.com/emojione/emojione/blob/master/extras/alpha-codes/LICENSE.md))
* Catch test framework ([Boost License](https://github.com/philsquared/Catch/blob/master/LICENSE.txt))
* xxHash ([BSD License](https://github.com/Cyan4973/xxHash/blob/dev/LICENSE))
* QR Code generator ([MIT License](https://github.com/nayuki/QR-Code-generator#license))
* CMake ([New BSD License](https://github.com/Kitware/CMake/blob/master/Copyright.txt))
* Hunspell ([LGPL](https://github.com/hunspell/hunspell/blob/master/COPYING.LESSER))
## Build instructions
* Windows [(32-bit)][win32] [(64-bit)][win64]
* [macOS][mac]
* [GNU/Linux using Docker][linux]
[//]: # (LINKS)
[telegram]: https://telegram.org
[telegram_desktop]: https://desktop.telegram.org
[telegram_api]: https://core.telegram.org
[telegram_proto]: https://core.telegram.org/mtproto
[license]: LICENSE
[win32]: docs/building-win.md
[win64]: docs/building-win-x64.md
[mac]: docs/building-mac.md
[linux]: docs/building-linux.md
[preview_image]: https://github.com/telegramdesktop/tdesktop/blob/dev/docs/assets/preview.png "Preview of Telegram Desktop"
[preview_image_url]: https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/docs/assets/preview.png

View file

@ -95,6 +95,30 @@ nice_target_sources(Telegram ${src_loc}
PRIVATE PRIVATE
${style_files} ${style_files}
ayu/qserializer.h
ayu/ayu_state.cpp
ayu/ayu_state.h
ayu/ayu_settings.cpp
ayu/ayu_settings.h
ayu/boxes/confirmation_box.cpp
ayu/boxes/confirmation_box.h
ayu/boxes/edit_deleted_mark.cpp
ayu/boxes/edit_deleted_mark.h
ayu/boxes/edit_edited_mark.cpp
ayu/boxes/edit_edited_mark.h
ayu/context_menu/context_menu.cpp
ayu/context_menu/context_menu.h
ayu/context_menu/message_history_box.cpp
ayu/context_menu/message_history_box.h
ayu/settings/settings_ayu.cpp
ayu/settings/settings_ayu.h
ayu/sqlite/sqlite3.c
ayu/sqlite/sqlite3.h
ayu/sqlite/sqlite_orm.h
ayu/database/entities.h
ayu/database/ayu_database.cpp
ayu/database/ayu_database.h
api/api_attached_stickers.cpp api/api_attached_stickers.cpp
api/api_attached_stickers.h api/api_attached_stickers.h
api/api_authorizations.cpp api/api_authorizations.cpp
@ -1560,9 +1584,9 @@ else()
set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:Debug>") set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:Debug>")
set(bundle_entitlements "Telegram.entitlements") set(bundle_entitlements "Telegram.entitlements")
if (LINUX AND DESKTOP_APP_USE_PACKAGED) if (LINUX AND DESKTOP_APP_USE_PACKAGED)
set(output_name "telegram-desktop") set(output_name "ayugram-desktop")
else() else()
set(output_name "Telegram") set(output_name "AyuGram")
endif() endif()
endif() endif()
@ -1601,7 +1625,7 @@ PRIVATE
if (APPLE if (APPLE
OR "${CMAKE_GENERATOR}" STREQUAL "Ninja Multi-Config" OR "${CMAKE_GENERATOR}" STREQUAL "Ninja Multi-Config"
OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL "" OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL ""
OR NOT "${output_name}" STREQUAL "Telegram") OR NOT "${output_name}" STREQUAL "AyuGram")
set(output_folder ${CMAKE_BINARY_DIR}) set(output_folder ${CMAKE_BINARY_DIR})
else() else()
set(output_folder ${CMAKE_BINARY_DIR}/bin) set(output_folder ${CMAKE_BINARY_DIR}/bin)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -60,11 +60,11 @@ BEGIN
BEGIN BEGIN
BLOCK "040904b0" BLOCK "040904b0"
BEGIN BEGIN
VALUE "CompanyName", "Telegram FZ-LLC" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "Telegram Desktop" VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "4.8.3.0" VALUE "FileVersion", "4.8.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.8.3.0" VALUE "ProductVersion", "4.8.3.0"
END END
END END

View file

@ -51,11 +51,11 @@ BEGIN
BEGIN BEGIN
BLOCK "040904b0" BLOCK "040904b0"
BEGIN BEGIN
VALUE "CompanyName", "Telegram FZ-LLC" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "Telegram Desktop Updater" VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "4.8.3.0" VALUE "FileVersion", "4.8.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.8.3.0" VALUE "ProductVersion", "4.8.3.0"
END END
END END

View file

@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "ayu/ayu_settings.h"
namespace Api { namespace Api {
namespace { namespace {
@ -112,6 +114,14 @@ void SendProgressManager::send(const Key &key, int progress) {
if (skipRequest(key)) { if (skipRequest(key)) {
return; return;
} }
// AyuGram sendUploadProgress
const auto settings = &AyuSettings::getInstance();
if (!settings->sendUploadProgress) {
DEBUG_LOG(("[AyuGram] Don't send upload progress"));
return;
}
using Type = SendProgressType; using Type = SendProgressType;
const auto action = [&]() -> MTPsendMessageAction { const auto action = [&]() -> MTPsendMessageAction {
const auto p = MTP_int(progress); const auto p = MTP_int(progress);

View file

@ -57,6 +57,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "ui/text/format_values.h" // Ui::FormatPhone #include "ui/text/format_values.h" // Ui::FormatPhone
#include "ayu/ayu_settings.h"
namespace Api { namespace Api {
namespace { namespace {
@ -899,8 +901,17 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
Core::App().checkAutoLock(lastNonIdleTime); Core::App().checkAutoLock(lastNonIdleTime);
}); });
const auto &config = _session->serverConfig(); // AyuGram sendOnlinePackets
bool isOnline = Core::App().hasActiveWindow(&session()); const auto settings = &AyuSettings::getInstance();
const auto &config = _session->serverConfig();
bool isOnlineOrig = Core::App().hasActiveWindow(&session());
bool isOnline = settings->sendOnlinePackets && isOnlineOrig;
// AyuGram sendOfflinePacketAfterOnline
if (settings->sendOfflinePacketAfterOnline && _lastWasOnline) {
isOnline = false;
}
int updateIn = config.onlineUpdatePeriod; int updateIn = config.onlineUpdatePeriod;
Assert(updateIn >= 0); Assert(updateIn >= 0);
if (isOnline) { if (isOnline) {
@ -922,7 +933,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|| (isOnline && gotOtherOffline)) { || (isOnline && gotOtherOffline)) {
api().request(base::take(_onlineRequest)).cancel(); api().request(base::take(_onlineRequest)).cancel();
_lastWasOnline = isOnline; _lastWasOnline = isOnlineOrig;
_lastSetOnline = ms; _lastSetOnline = ms;
if (!Core::Quitting()) { if (!Core::Quitting()) {
_onlineRequest = api().request(MTPaccount_UpdateStatus( _onlineRequest = api().request(MTPaccount_UpdateStatus(
@ -943,7 +954,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
session().changes().peerUpdated( session().changes().peerUpdated(
self, self,
Data::PeerUpdate::Flag::OnlineStatus); Data::PeerUpdate::Flag::OnlineStatus);
if (!isOnline) { // Went offline, so we need to save message draft to the cloud. if (!isOnlineOrig) { // Went offline, so we need to save message draft to the cloud.
api().saveCurrentDraftToCloud(); api().saveCurrentDraftToCloud();
session().data().maybeStopWatchForOffline(self); session().data().maybeStopWatchForOffline(self);
} }
@ -953,6 +964,22 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
updateIn = qMin(updateIn, int(_lastSetOnline + config.onlineUpdatePeriod - ms)); updateIn = qMin(updateIn, int(_lastSetOnline + config.onlineUpdatePeriod - ms));
Assert(updateIn >= 0); Assert(updateIn >= 0);
} }
// AyuGram sendOfflinePacketAfterOnline
if (settings->sendOfflinePacketAfterOnline) {
session().api().requestFullPeer(session().user());
if (session().user()->onlineTill > base::unixtime::now()) {
DEBUG_LOG(("[AyuGram] User likely appeared online"));
_onlineRequest = api().request(MTPaccount_UpdateStatus(
MTP_bool(true)
)).send();
}
DEBUG_LOG(("[AyuGram] Decreasing updateIn because of enabled features"));
updateIn = 1250;
}
_onlineTimer.callOnce(updateIn); _onlineTimer.callOnce(updateIn);
} }

View file

@ -98,6 +98,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_media_prepare.h" #include "storage/storage_media_prepare.h"
#include "storage/storage_account.h" #include "storage/storage_account.h"
#include "ayu/ayu_settings.h"
namespace { namespace {
// Save draft to the cloud with 1 sec extra delay. // Save draft to the cloud with 1 sec extra delay.
@ -3483,6 +3485,14 @@ void ApiWrap::sendUploadedPhoto(
Api::RemoteFileInfo info, Api::RemoteFileInfo info,
Api::SendOptions options) { Api::SendOptions options) {
if (const auto item = _session->data().message(localId)) { if (const auto item = _session->data().message(localId)) {
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !options.scheduled) {
DEBUG_LOG(("[AyuGram] Scheduling message"));
auto current = base::unixtime::now();
options.scheduled = current + 18; // using 18 seconds because photo can be big
}
const auto media = Api::PrepareUploadedPhoto(item, std::move(info)); const auto media = Api::PrepareUploadedPhoto(item, std::move(info));
if (const auto groupId = item->groupId()) { if (const auto groupId = item->groupId()) {
uploadAlbumMedia(item, groupId, media); uploadAlbumMedia(item, groupId, media);
@ -3500,6 +3510,15 @@ void ApiWrap::sendUploadedDocument(
if (!item->media() || !item->media()->document()) { if (!item->media() || !item->media()->document()) {
return; return;
} }
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !options.scheduled) {
DEBUG_LOG(("[AyuGram] Scheduling message"));
auto current = base::unixtime::now();
options.scheduled = current + 60; // well, a document can be really big...
}
const auto media = Api::PrepareUploadedDocument( const auto media = Api::PrepareUploadedDocument(
item, item,
std::move(info)); std::move(info));
@ -3911,6 +3930,14 @@ void ApiWrap::sendMediaWithRandomId(
const MTPInputMedia &media, const MTPInputMedia &media,
Api::SendOptions options, Api::SendOptions options,
uint64 randomId) { uint64 randomId) {
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !options.scheduled) {
DEBUG_LOG(("[AyuGram] Scheduling message"));
auto current = base::unixtime::now();
options.scheduled = current + 12;
}
const auto history = item->history(); const auto history = item->history();
const auto replyTo = item->replyToId(); const auto replyTo = item->replyToId();
const auto topicRootId = item->topicRootId(); const auto topicRootId = item->topicRootId();
@ -4003,6 +4030,15 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
_sendingAlbums.remove(groupId); _sendingAlbums.remove(groupId);
return; return;
} }
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !album->options.scheduled) {
DEBUG_LOG(("[AyuGram] Scheduling message"));
auto current = base::unixtime::now();
album->options.scheduled = current + 12;
}
auto sample = (HistoryItem*)nullptr; auto sample = (HistoryItem*)nullptr;
auto medias = QVector<MTPInputSingleMedia>(); auto medias = QVector<MTPInputSingleMedia>();
medias.reserve(album->items.size()); medias.reserve(album->items.size());

View file

@ -0,0 +1,43 @@
#include "ayu_settings.h"
namespace AyuSettings {
const QString filename = "tdata/ayu_settings.json";
const int latestMigration = 1;
std::optional<AyuGramSettings> settings = std::nullopt;
AyuGramSettings &getInstance() {
if (!settings.has_value()) {
settings = std::optional(AyuGramSettings());
}
return settings.value();
}
void load() {
QFile file(filename);
if (!file.exists()) {
return;
}
file.open(QIODevice::ReadOnly);
QByteArray json = file.readAll();
file.close();
if (!settings.has_value()) {
settings = std::optional(AyuGramSettings());
}
settings->fromJson(json);
}
void save() {
if (!settings.has_value()) {
settings = std::optional(AyuGramSettings());
}
settings->migrationVersion = latestMigration;
QByteArray json = settings->toRawJson();
QFile file(filename);
file.open(QIODevice::WriteOnly);
file.write(json);
file.close();
}
}

View file

@ -0,0 +1,103 @@
#pragma once
#define QS_HAS_JSON
#include <lang_auto.h>
#include "qserializer.h"
namespace AyuSettings {
class AyuGramSettings : public QSerializer {
Q_GADGET
public:
AyuGramSettings() {
migrationVersion = 0;
sendReadPackets = true;
sendOnlinePackets = true;
sendOfflinePacketAfterOnline = false;
sendUploadProgress = true;
useScheduledMessages = false;
keepDeletedMessages = false;
keepMessagesHistory = false;
deletedMark = "🧹";
editedMark = tr::lng_edited(tr::now);
ghostMode = true;
}
QS_SERIALIZABLE
QS_FIELD(int, migrationVersion)
QS_FIELD(bool, sendReadPackets)
QS_FIELD(bool, sendOnlinePackets)
QS_FIELD(bool, sendOfflinePacketAfterOnline)
QS_FIELD(bool, sendUploadProgress)
QS_FIELD(bool, useScheduledMessages)
QS_FIELD(bool, keepDeletedMessages)
QS_FIELD(bool, keepMessagesHistory)
QS_FIELD(QString, deletedMark)
QS_FIELD(QString, editedMark)
QS_FIELD(bool, ghostMode)
public:
void set_migrationVersion(int val) {
migrationVersion = val;
}
void set_sendReadPackets(bool val) {
sendReadPackets = val;
}
void set_sendOnlinePackets(bool val) {
sendOnlinePackets = val;
}
void set_sendOfflinePacketAfterOnline(bool val) {
sendOfflinePacketAfterOnline = val;
}
void set_sendUploadProgress(bool val) {
sendUploadProgress = val;
}
void set_useScheduledMessages(bool val) {
useScheduledMessages = val;
}
void set_keepDeletedMessages(bool val) {
keepDeletedMessages = val;
}
void set_keepMessagesHistory(bool val) {
keepMessagesHistory = val;
}
void set_deletedMark(QString val) {
deletedMark = val;
}
void set_editedMark(QString val) {
editedMark = val;
}
void set_ghostMode(bool val) {
ghostMode = val;
}
};
AyuGramSettings &getInstance();
void load();
void save();
}

View file

@ -0,0 +1,13 @@
#include "ayu_state.h"
namespace AyuState {
void setAllowSendReadPacket(bool val, int resetAfter) {
allowSendReadPacket.val = val;
allowSendReadPacket.resetAfter = resetAfter;
}
bool getAllowSendPacket() {
auto settings = &AyuSettings::getInstance();
return settings->sendReadPackets || processVariable(allowSendReadPacket);
}
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "ayu_settings.h"
namespace AyuState {
namespace {
class AyuStateVariable {
public:
bool val;
int resetAfter;
};
AyuStateVariable allowSendReadPacket;
bool processVariable(AyuStateVariable& variable) {
if (variable.resetAfter == -1) {
return variable.val;
}
variable.resetAfter -= 1;
auto val = variable.val;
if (variable.resetAfter == 0) {
variable.val = false;
}
return val;
}
}
void setAllowSendReadPacket(bool val, int resetAfter = 1);
bool getAllowSendPacket();
}

View file

@ -0,0 +1,38 @@
#include "confirmation_box.h"
#include "ayu/ayu_settings.h"
#include "window/window_session_controller.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "window/window_peer_menu.h"
#include "styles/style_layers.h"
#include "lang_auto.h"
namespace AyuUi {
ConfirmationBox::ConfirmationBox(
QWidget*,
not_null<Window::SessionController *> controller) : _controller(controller) {
//
}
void ConfirmationBox::prepare() {
setTitle(rpl::single(QString("Confirmation for SRead")));
setDimensions(st::boxWideWidth, st::boxPadding.top());
addButton(rpl::single(QString("OK")), [=, this] {
ReadAllPeers();
closeBox();
});
addButton(tr::lng_cancel(), [=, this] { closeBox(); });
}
void ConfirmationBox::ReadAllPeers() {
auto settings = &AyuSettings::getInstance();
auto prev = settings->sendReadPackets;
settings->set_sendReadPackets(true);
auto chats = _controller->session().data().chatsList();
Window::MarkAsReadChatListHack(chats);
settings->set_sendReadPackets(prev);
}
}

View file

@ -0,0 +1,14 @@
#include "ui/layers/box_content.h"
#include "window/window_main_menu.h"
namespace AyuUi {
class ConfirmationBox : public Ui::BoxContent {
public:
ConfirmationBox(QWidget*, not_null<Window::SessionController *> controller);
protected:
void prepare() override;
private:
void ReadAllPeers();
not_null<Window::SessionController*> _controller;
};
}

View file

@ -0,0 +1,82 @@
#include "edit_deleted_mark.h"
#include "lang/lang_keys.h"
#include "base/random.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/premium_limits_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/special_fields.h"
#include "ui/widgets/popup_menu.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
#include "data/data_cloud_file.h"
#include "main/main_session.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_widgets.h"
#include <QtGui/QGuiApplication>
#include "storage/localstorage.h"
#include "ayu/ayu_settings.h"
EditDeletedMarkBox::EditDeletedMarkBox(QWidget *) :
_text(
this,
st::defaultInputField,
rpl::single(QString("Deleted Mark")),
AyuSettings::getInstance().deletedMark) {
}
void EditDeletedMarkBox::prepare() {
const auto defaultDeletedMark = "🧹";
auto newHeight = st::contactPadding.top() + _text->height();
setTitle(rpl::single(QString("Edit Deleted Mark")));
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWidth, newHeight);
addLeftButton(rpl::single(QString("Reset")), [=] { _text->setText(defaultDeletedMark); });
addButton(tr::lng_settings_save(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
connect(_text, &Ui::InputField::submitted, [=] { submit(); });
}
void EditDeletedMarkBox::setInnerFocus() {
_text->setFocusFast();
}
void EditDeletedMarkBox::submit() {
if (_text->getLastText().trimmed().isEmpty()) {
_text->setFocus();
_text->showError();
} else {
save();
}
}
void EditDeletedMarkBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_text->resize(
width()
- st::boxPadding.left()
- st::newGroupInfoPadding.left()
- st::boxPadding.right(),
_text->height());
const auto left = st::boxPadding.left() + st::newGroupInfoPadding.left();
_text->moveToLeft(left, st::contactPadding.top());
}
void EditDeletedMarkBox::save() {
const auto settings = &AyuSettings::getInstance();
settings->deletedMark = _text->getLastText();
Local::writeSettings();
closeBox();
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "mtproto/sender.h"
class EditDeletedMarkBox : public Ui::BoxContent {
public:
EditDeletedMarkBox(QWidget*);
protected:
void setInnerFocus() override;
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
private:
void submit();
void save();
object_ptr<Ui::InputField> _text;
};

View file

@ -0,0 +1,85 @@
#include "edit_edited_mark.h"
#include "lang/lang_keys.h"
#include "base/random.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/premium_limits_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/special_fields.h"
#include "ui/widgets/popup_menu.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
#include "data/data_cloud_file.h"
#include "main/main_session.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_widgets.h"
#include <QtGui/QGuiApplication>
#include "storage/localstorage.h"
#include "ayu/ayu_settings.h"
EditEditedMarkBox::EditEditedMarkBox(QWidget *) :
_text(
this,
st::defaultInputField,
rpl::single(QString("Edited mark")),
AyuSettings::getInstance().editedMark) {
}
void EditEditedMarkBox::prepare() {
const auto defaultEditedMark = tr::lng_edited(tr::now);
auto newHeight = st::contactPadding.top() + _text->height();
setTitle(rpl::single(QString("Edit edited Mark")));
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWidth, newHeight);
addLeftButton(rpl::single(QString("Reset")), [=] { _text->setText(defaultEditedMark); });
addButton(tr::lng_settings_save(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
connect(_text, &Ui::InputField::submitted, [=] { submit(); });
}
void EditEditedMarkBox::setInnerFocus() {
_text->setFocusFast();
}
void EditEditedMarkBox::submit() {
if (_text->getLastText().trimmed().isEmpty()) {
_text->setFocus();
_text->showError();
} else {
save();
}
}
void EditEditedMarkBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_text->resize(
width()
- st::boxPadding.left()
- st::newGroupInfoPadding.left()
- st::boxPadding.right(),
_text->height());
const auto left = st::boxPadding.left() + st::newGroupInfoPadding.left();
_text->moveToLeft(left, st::contactPadding.top());
}
void EditEditedMarkBox::save() {
const auto settings = &AyuSettings::getInstance();
settings->editedMark = _text->getLastText();
Local::writeSettings();
closeBox();
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "mtproto/sender.h"
class EditEditedMarkBox : public Ui::BoxContent {
public:
EditEditedMarkBox(QWidget*);
protected:
void setInnerFocus() override;
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
private:
void submit();
void save();
object_ptr<Ui::InputField> _text;
};

View file

@ -0,0 +1,44 @@
#include "context_menu.h"
#include "history/history_inner_widget.h"
#include "ui/widgets/popup_menu.h"
#include "base/unixtime.h"
#include "styles/style_chat.h"
#include "settings/settings_common.h"
#include "message_history_box.h"
#include "ayu/database/ayu_database.h"
#include "ayu/ayu_state.h"
namespace AyuUi {
AyuPopupMenu::AyuPopupMenu(HistoryInner *parent) {
_ayuSubMenu = std::make_unique<Ui::PopupMenu>(parent, st::popupMenuWithIcons);
}
void AyuPopupMenu::addHistoryAction(HistoryItem *item) {
if (AyuDatabase::editedMessagesTableExists() && !((AyuDatabase::getEditedMessages(item)).empty())) {
_ayuSubMenu->addAction(QString("History"), [=] {
auto box = Box<MessageHistoryBox>(item);
Ui::show(std::move(box));
}, &st::menuIconInfo);
}
}
void AyuPopupMenu::addHideMessageAction(HistoryItem *item) const {
const auto settings = &AyuSettings::getInstance();
const auto history = item->history();
_ayuSubMenu->addAction(QString("Hide"), [=]() {
const auto initKeepDeleted = settings->keepDeletedMessages;
settings->set_keepDeletedMessages(false);
history->destroyMessage(item);
settings->set_keepDeletedMessages(initKeepDeleted);
}, &st::menuIconClear);
}
void AyuPopupMenu::addReadUntilAction(HistoryItem *item) const {
const auto history = item->history();
_ayuSubMenu->addAction(QString("Read until"), [=]() {
AyuState::setAllowSendReadPacket(true);
history->session().data().histories().readInboxOnNewMessage(item);
}, &st::menuIconShowInChat);
}
} // namespace AyuUi

View file

@ -0,0 +1,92 @@
#pragma once
#include "history/history_inner_widget.h"
#include "ui/widgets/popup_menu.h"
#include "ui/image/image.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/effects/message_sending_animation_controller.h"
#include "ui/effects/reaction_fly_animation.h"
#include "ui/text/text_options.h"
#include "ui/text/text_entity.h"
#include "ui/boxes/report_box.h"
#include "ui/layers/generic_box.h"
#include "ui/controls/delete_message_context_action.h"
#include "ui/controls/who_reacted_context_action.h"
#include "ui/painter.h"
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
#include "ui/inactive_press.h"
#include "window/window_adaptive.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "window/window_peer_menu.h"
#include "window/window_controller.h"
#include "window/notifications_manager.h"
#include "boxes/about_sponsored_box.h"
#include "boxes/delete_messages_box.h"
#include "boxes/report_messages_box.h"
#include "boxes/sticker_set_box.h"
#include "boxes/premium_preview_box.h"
#include "boxes/translate_box.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/emoji_interactions.h"
#include "history/history_widget.h"
#include "history/view/history_view_translate_tracker.h"
#include "base/platform/base_platform_info.h"
#include "base/qt/qt_common_adapters.h"
#include "base/qt/qt_key_modifiers.h"
#include "base/unixtime.h"
#include "base/call_delayed.h"
#include "mainwindow.h"
#include "layout/layout_selection.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "main/session/send_as_peers.h"
#include "menu/menu_item_download_files.h"
#include "core/application.h"
#include "lang/lang_keys.h"
#include "data/data_session.h"
#include "data/data_media_types.h"
#include "data/data_message_reactions.h"
#include "data/data_document.h"
#include "data/data_channel.h"
#include "data/data_forum_topic.h"
#include "data/data_poll.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_peer_values.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "data/data_changes.h"
#include "data/stickers/data_stickers.h"
#include "data/data_sponsored_messages.h"
#include "dialogs/ui/dialogs_video_userpic.h"
#include "settings/settings_premium.h"
#include "styles/style_chat.h"
#include "styles/style_window.h" // st::windowMinWidth
#include "styles/style_menu_icons.h"
#include "history/view/history_view_context_menu.h"
#include <styles/style_info.h>
#include <ayu/ayu_settings.h>
namespace AyuUi {
class AyuPopupMenu {
public:
AyuPopupMenu(HistoryInner *parent);
void addHistoryAction(HistoryItem *item);
void addHideMessageAction(HistoryItem *item) const;
void addReadUntilAction(HistoryItem *item) const;
std::unique_ptr<Ui::PopupMenu> _ayuSubMenu;
};
}

View file

@ -0,0 +1,62 @@
#include <styles/style_layers.h>
#include <styles/style_boxes.h>
#include <ui/effects/scroll_content_shadow.h>
#include <styles/style_settings.h>
#include "message_history_box.h"
#include "settings/settings_common.h"
#include "ayu/ayu_settings.h"
#include "ayu/database/ayu_database.h"
#include "history/history.h"
using namespace Settings;
namespace AyuUi {
MessageHistoryBox::MessageHistoryBox(QWidget *, HistoryItem *item)
: _content(this), _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll)) {
setupControls();
addEditedMessagesToLayout(item);
}
void MessageHistoryBox::setupControls() {
_content.create(this);
_content->resizeToWidth(st::boxWideWidth);
_content->moveToLeft(0, 0);
_content->heightValue(
) | rpl::start_to_stream(_contentHeight, _content->lifetime());
_scroll->setOwnedWidget(
object_ptr<Ui::RpWidget>::fromRaw(_content));
}
void MessageHistoryBox::resizeEvent(QResizeEvent *e) {
_scroll->resize(width(), height() - st::boxPhotoPadding.top() - st::boxPadding.bottom());
_scroll->move(0, st::boxPadding.top());
if (_content) {
_content->resize(_scroll->width(), _content->height());
}
}
void MessageHistoryBox::prepare() {
setTitle(rpl::single(QString("Message history")));
// setDimensionsToContent(st::boxWideWidth, _content);
setDimensions(st::boxWideWidth, 900);
Ui::SetupShadowsToScrollContent(this, _scroll, _contentHeight.events());
}
void MessageHistoryBox::addEditedMessagesToLayout(HistoryItem *item) {
auto messages = AyuDatabase::getEditedMessages(item);
if (messages.empty()) {
return;
}
for (const auto& message : messages) {
AddSkip(_content);
AddDividerText(_content, rpl::single(QString::fromStdString(message.text)));
AddSkip(_content);
}
}
}

View file

@ -0,0 +1,22 @@
#include <ui/layers/box_content.h>
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/scroll_area.h"
#include "history/history_item.h"
namespace AyuUi {
class MessageHistoryBox : public Ui::BoxContent {
public:
MessageHistoryBox(QWidget*, HistoryItem *item);
protected:
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
private:
void setupControls();
void addEditedMessagesToLayout(HistoryItem *item);
object_ptr<Ui::VerticalLayout> _content;
const base::unique_qptr<Ui::ScrollArea> _scroll;
rpl::event_stream<int> _contentHeight;
};
}

View file

@ -0,0 +1,74 @@
#include "ayu_database.h"
#include "entities.h"
#include "ayu/sqlite/sqlite_orm.h"
#include "QtCore/QString"
#include "main/main_session.h"
using namespace sqlite_orm;
auto storage = make_storage("ayugram.db",
make_table("editedmessage",
make_column("userId", &EditedMessage::userId),
make_column("dialogId", &EditedMessage::dialogId),
make_column("messageId", &EditedMessage::messageId),
make_column("text", &EditedMessage::text),
make_column("isDocument", &EditedMessage::isDocument),
make_column("path", &EditedMessage::path),
make_column("date", &EditedMessage::date)
)
);
namespace AyuDatabase {
void addEditedMessage(
long userId,
long dialogId,
long messageId,
const QString &text,
bool isDocument,
QString path,
long date) {
EditedMessage entity;
entity.userId = userId;
entity.dialogId = dialogId;
entity.messageId = messageId;
entity.text = text.toStdString();
entity.isDocument = isDocument;
entity.path = path.toStdString();
entity.date = date;
storage.sync_schema();
storage.begin_transaction();
storage.insert(entity);
storage.commit();
}
std::vector<EditedMessage> getEditedMessages(
long userId,
long dialogId,
long messageId
) {
return storage.get_all<EditedMessage>(
where(
c(&EditedMessage::userId) == userId and
c(&EditedMessage::dialogId) == dialogId and
c(&EditedMessage::messageId) == messageId)
);
}
std::vector<EditedMessage> getEditedMessages(HistoryItem *item) {
auto userId = item->displayFrom()->id.value;
auto dialogId = item->history()->peer->id.value;
auto msgId = item->id.bare;
// auto some = &item->history()->session().account();
return getEditedMessages((long) userId, (long) dialogId, (long) msgId);
}
bool editedMessagesTableExists() {
return storage.table_exists("editedmessage");
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "entities.h"
#include "history/history_item.h"
#include "history/history.h"
namespace AyuDatabase {
void addEditedMessage(
long userId,
long dialogId,
long messageId,
const QString& text,
bool isDocument,
QString path,
long date);
std::vector<EditedMessage> getEditedMessages(
long userId,
long dialogId,
long messageId
);
std::vector<EditedMessage> getEditedMessages(HistoryItem *item);
bool editedMessagesTableExists();
}

View file

@ -0,0 +1,17 @@
#pragma once
#include <string>
// https://github.com/AyuGram/AyuGram4A/blob/main/TMessagesProj/src/main/java/com/radolyn/ayugram/database/entities/EditedMessage.java
class EditedMessage {
public:
long userId;
long dialogId;
long messageId;
std::string text;
bool isDocument;
std::string path;
long date;
};

View file

@ -0,0 +1,849 @@
/*
MIT License
Copyright (c) 2020-2021 Agadzhanov Vladimir
Portions Copyright (c) 2021 Jerry Jacobs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef QSERIALIZER_H
#define QSERIALIZER_H
/* JSON */
#ifdef QS_HAS_JSON
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonValue>
#endif
/* XML */
#ifdef QS_HAS_XML
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#endif
/* META OBJECT SYSTEM */
#include <QVariant>
#include <QMetaProperty>
#include <QMetaObject>
#include <QMetaType>
#include <type_traits>
#include <QDebug>
#define QS_VERSION "1.2"
/* Generate metaObject method */
#define QS_META_OBJECT_METHOD \
virtual const QMetaObject * metaObject() const { \
return &this->staticMetaObject; \
} \
#define QSERIALIZABLE \
Q_GADGET \
QS_META_OBJECT_METHOD
/* Mark class as serializable */
#define QS_SERIALIZABLE QS_META_OBJECT_METHOD
#ifdef QS_HAS_XML
Q_DECLARE_METATYPE(QDomNode)
Q_DECLARE_METATYPE(QDomElement)
#endif
class QSerializer {
Q_GADGET
QS_SERIALIZABLE
public:
virtual ~QSerializer() = default;
#ifdef QS_HAS_JSON
/*! \brief Convert QJsonValue in QJsonDocument as QByteArray. */
static QByteArray toByteArray(const QJsonValue & value){
return QJsonDocument(value.toObject()).toJson();
}
#endif
#ifdef QS_HAS_XML
/*! \brief Convert QDomNode in QDomDocument as QByteArray. */
static QByteArray toByteArray(const QDomNode & value) {
QDomDocument doc = value.toDocument();
return doc.toByteArray();
}
/*! \brief Make xml processing instruction (hat) and returns new XML QDomDocument. On deserialization procedure all processing instructions will be ignored. */
static QDomDocument appendXmlHat(const QDomNode &node, const QString & encoding, const QString & version = "1.0"){
QDomDocument doc = node.toDocument();
QDomNode xmlNode = doc.createProcessingInstruction("xml", QString("version=\"%1\" encoding=\"%2\"").arg(version).arg(encoding));
doc.insertBefore(xmlNode, doc.firstChild());
return doc;
}
#endif
#ifdef QS_HAS_JSON
/*! \brief Serialize all accessed JSON propertyes for this object. */
QJsonObject toJson() const {
QJsonObject json;
for(int i = 0; i < metaObject()->propertyCount(); i++)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
continue;
}
#else
if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
continue;
}
#endif
json.insert(metaObject()->property(i).name(), metaObject()->property(i).readOnGadget(this).toJsonValue());
}
return json;
}
/*! \brief Returns QByteArray representation this object using json-serialization. */
QByteArray toRawJson() const {
return toByteArray(toJson());
}
/*! \brief Deserialize all accessed XML propertyes for this object. */
void fromJson(const QJsonValue & val) {
if(val.isObject())
{
QJsonObject json = val.toObject();
QStringList keys = json.keys();
int propCount = metaObject()->propertyCount();
for(int i = 0; i < propCount; i++)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
continue;
}
#else
if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
continue;
}
#endif
for(auto key : json.keys())
{
if(key == metaObject()->property(i).name())
{
metaObject()->property(i).writeOnGadget(this, json.value(key));
break;
}
}
}
}
}
/*! \brief Deserialize all accessed JSON propertyes for this object. */
void fromJson(const QByteArray & data) {
fromJson(QJsonDocument::fromJson(data).object());
}
#endif // QS_HAS_JSON
#ifdef QS_HAS_XML
/*! \brief Serialize all accessed XML propertyes for this object. */
QDomNode toXml() const {
QDomDocument doc;
QDomElement el = doc.createElement(metaObject()->className());
for(int i = 0; i < metaObject()->propertyCount(); i++)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QDomNode>())) {
continue;
}
#else
if(metaObject()->property(i).metaType().id() != qMetaTypeId<QDomNode>()) {
continue;
}
#endif
el.appendChild(QDomNode(metaObject()->property(i).readOnGadget(this).value<QDomNode>()));
}
doc.appendChild(el);
return doc;
}
/*! \brief Returns QByteArray representation this object using xml-serialization. */
QByteArray toRawXml() const {
return toByteArray(toXml());
}
/*! \brief Deserialize all accessed XML propertyes for this object. */
void fromXml(const QDomNode & val) {
QDomNode doc = val;
auto n = doc.firstChildElement(metaObject()->className());
if(!n.isNull()) {
for(int i = 0; i < metaObject()->propertyCount(); i++) {
QString name = metaObject()->property(i).name();
QDomElement tmp = metaObject()->property(i).readOnGadget(this).value<QDomNode>().firstChildElement();
auto f = n.firstChildElement(tmp.tagName());
metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
}
}
else
{
for(int i = 0; i < metaObject()->propertyCount(); i++) {
QString name = metaObject()->property(i).name();
auto f = doc.firstChildElement(name);
metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
}
}
}
/*! \brief Deserialize all accessed XML propertyes for this object. */
void fromXml(const QByteArray & data) {
QDomDocument d;
d.setContent(data);
fromXml(d);
}
#endif // QS_HAS_XML
};
#define GET(prefix, name) get_##prefix##_##name
#define SET(prefix, name) set_##prefix##_##name
/* Create variable */
#define QS_DECLARE_MEMBER(type, name) \
public : \
type name = type(); \
/* Create JSON property and methods for primitive type field*/
#ifdef QS_HAS_JSON
#define QS_JSON_FIELD(type, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonValue val = QJsonValue::fromVariant(QVariant(name)); \
return val; \
} \
void SET(json, name)(const QJsonValue & varname){ \
name = varname.toVariant().value<type>(); \
}
#else
#define QS_JSON_FIELD(type, name)
#endif
/* Create XML property and methods for primitive type field*/
#ifdef QS_HAS_XML
#define QS_XML_FIELD(type, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QString strname = #name; \
QDomElement element = doc.createElement(strname); \
QDomText valueOfProp = doc.createTextNode(QVariant(name).toString()); \
element.appendChild(valueOfProp); \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode &node) { \
if(!node.isNull() && node.isElement()){ \
QDomElement domElement = node.toElement(); \
if(domElement.tagName() == #name) \
name = QVariant(domElement.text()).value<type>(); \
} \
}
#else
#define QS_XML_FIELD(type, name)
#endif
/* Generate JSON-property and methods for primitive type objects */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_JSON
#define QS_JSON_ARRAY(itemType, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonArray val; \
for(int i = 0; i < name.size(); i++) \
val.push_back(name.at(i)); \
return QJsonValue::fromVariant(val); \
} \
void SET(json, name)(const QJsonValue & varname) { \
if(!varname.isArray()) \
return; \
name.clear(); \
QJsonArray val = varname.toArray(); \
for(auto item : val) { \
itemType tmp; \
tmp = item.toVariant().value<itemType>(); \
name.append(tmp); \
} \
}
#else
#define QS_JSON_ARRAY(itemType, name)
#endif
/* Generate XML-property and methods for primitive type objects */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_XML
#define QS_XML_ARRAY(itemType, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QString strname = #name; \
QDomElement arrayXml = doc.createElement(QString(strname)); \
arrayXml.setAttribute("type", "array"); \
\
for(int i = 0; i < name.size(); i++) { \
itemType item = name.at(i); \
QDomElement itemXml = doc.createElement("item"); \
itemXml.setAttribute("type", #itemType); \
itemXml.setAttribute("index", i); \
itemXml.appendChild(doc.createTextNode(QVariant(item).toString())); \
arrayXml.appendChild(itemXml); \
} \
\
doc.appendChild(arrayXml); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
QDomNode domNode = node.firstChild(); \
name.clear(); \
while(!domNode.isNull()) { \
if(domNode.isElement()) { \
QDomElement domElement = domNode.toElement(); \
name.append(QVariant(domElement.text()).value<itemType>()); \
} \
domNode = domNode.nextSibling(); \
} \
}
#else
#define QS_XML_ARRAY(itemType, name)
#endif
/* Generate JSON-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
#ifdef QS_HAS_JSON
#define QS_JSON_OBJECT(type, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val = name.toJson(); \
return QJsonValue(val); \
} \
void SET(json, name)(const QJsonValue & varname) { \
if(!varname.isObject()) \
return; \
name.fromJson(varname); \
}
#else
#define QS_JSON_OBJECT(type, name)
#endif
/* Generate XML-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
#ifdef QS_HAS_XML
#define QS_XML_OBJECT(type, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
return name.toXml(); \
} \
void SET(xml, name)(const QDomNode & node){ \
name.fromXml(node); \
}
#else
#define QS_XML_OBJECT(type, name)
#endif
/* Generate JSON-property and methods for collection of custom type objects */
/* Custom item type must be provide methods fromJson and toJson or inherit from QSerializer */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_JSON
#define QS_JSON_ARRAY_OBJECTS(itemType, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonArray val; \
for(int i = 0; i < name.size(); i++) \
val.push_back(name.at(i).toJson()); \
return QJsonValue::fromVariant(val); \
} \
void SET(json, name)(const QJsonValue & varname) { \
if(!varname.isArray()) \
return; \
name.clear(); \
QJsonArray val = varname.toArray(); \
for(int i = 0; i < val.size(); i++) { \
itemType tmp; \
tmp.fromJson(val.at(i)); \
name.append(tmp); \
} \
}
#else
#define QS_JSON_ARRAY_OBJECTS(itemType, name)
#endif
/* Generate XML-property and methods for collection of custom type objects */
/* Custom type must be provide methods fromXml and toXml or inherit from QSerializer */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_XML
#define QS_XML_ARRAY_OBJECTS(itemType, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "array"); \
for(int i = 0; i < name.size(); i++) \
element.appendChild(name.at(i).toXml()); \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
name.clear(); \
QDomNodeList nodesList = node.childNodes(); \
for(int i = 0; i < nodesList.size(); i++) { \
itemType tmp; \
tmp.fromXml(nodesList.at(i)); \
name.append(tmp); \
} \
}
#else
#define QS_XML_ARRAY_OBJECTS(itemType, name)
#endif
/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...) */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
#ifdef QS_HAS_JSON
#define QS_JSON_QT_DICT(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p = name.constBegin(); p != name.constEnd(); ++p) { \
val.insert( \
QVariant(p.key()).toString(), \
QJsonValue::fromVariant(QVariant(p.value()))); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin() ;p != val.constEnd(); ++p) { \
name.insert( \
QVariant(p.key()).value<map::key_type>(), \
QVariant(p.value()).value<map::mapped_type>()); \
} \
}
#else
#define QS_JSON_QT_DICT(map, name)
#endif
/* Generate XML-property and methods for dictionary of simple fields (int, bool, QString, ...) */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
#ifdef QS_HAS_XML
#define QS_XML_QT_DICT(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p = name.begin(); p != name.end(); ++p) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.key()).toString()); \
e.setAttribute("value", QVariant(p.value()).toString()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
QVariant(item.attributeNode("value").value()).value<map::mapped_type>()); \
} \
} \
} \
}
#else
#define QS_XML_QT_DICT(map, name)
#endif
/* Generate JSON-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method inserv(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
#ifdef QS_HAS_JSON
#define QS_JSON_QT_DICT_OBJECTS(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p = name.begin(); p != name.end(); ++p) { \
val.insert( \
QVariant::fromValue(p.key()).toString(), \
p.value().toJson()); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin();p != val.constEnd(); ++p) { \
map::mapped_type tmp; \
tmp.fromJson(p.value()); \
name.insert( \
QVariant(p.key()).value<map::key_type>(), \
tmp); \
} \
}
#else
#define QS_JSON_QT_DICT_OBJECTS(map, name)
#endif
/* Generate XML-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
#ifdef QS_HAS_XML
#define QS_XML_QT_DICT_OBJECTS(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p = name.begin(); p != name.end(); ++p) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.key()).toString()); \
e.appendChild(p.value().toXml()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
map::mapped_type tmp; \
tmp.fromXml(item.firstChild()); \
name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
tmp); \
} \
} \
} \
}
#else
#define QS_XML_QT_DICT_OBJECTS(map, name)
#endif
/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...) */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, QString>, std::map<int,int>, ...*/
#ifdef QS_HAS_JSON
#define QS_JSON_STL_DICT(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p : name){ \
val.insert( \
QVariant::fromValue(p.first).toString(), \
QJsonValue::fromVariant(QVariant(p.second))); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin();p != val.constEnd(); ++p) { \
name.insert(std::pair<map::key_type, map::mapped_type>( \
QVariant(p.key()).value<map::key_type>(), \
QVariant(p.value()).value<map::mapped_type>())); \
} \
}
#else
#define QS_JSON_STL_DICT(map, name)
#endif
#ifdef QS_HAS_XML
#define QS_XML_STL_DICT(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p : name) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.first).toString()); \
e.setAttribute("value", QVariant(p.second).toString()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
name.insert(std::pair<map::key_type, map::mapped_type>( \
QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
QVariant(item.attributeNode("value").value()).value<map::mapped_type>())); \
} \
} \
} \
}
#else
#define QS_XML_STL_DICT(map, name)
#endif
/* Generate JSON-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
#ifdef QS_HAS_JSON
#define QS_JSON_STL_DICT_OBJECTS(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p : name){ \
val.insert( \
QVariant::fromValue(p.first).toString(), \
p.second.toJson()); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin(); p != val.constEnd(); ++p) { \
map::mapped_type tmp; \
tmp.fromJson(p.value()); \
name.insert(std::pair<map::key_type, map::mapped_type>( \
QVariant(p.key()).value<map::key_type>(), \
tmp)); \
} \
}
#else
#define QS_JSON_STL_DICT_OBJECTS(map, name)
#endif
/* Generate XML-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
#ifdef QS_HAS_XML
#define QS_XML_STL_DICT_OBJECTS(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p : name) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.first).toString()); \
e.appendChild(p.second.toXml()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
map::mapped_type tmp; \
tmp.fromXml(item.firstChild()); \
name.insert(std::pair<map::key_type, map::mapped_type> ( \
QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
tmp)); \
} \
} \
} \
}
#else
#define QS_XML_STL_DICT_OBJECTS(map, name)
#endif
/* BIND: */
/* generate serializable propertyes JSON and XML for primitive type field */
#define QS_BIND_FIELD(type, name) \
QS_JSON_FIELD(type, name) \
QS_XML_FIELD(type, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for collection of primitive type fields */
#define QS_BIND_COLLECTION(itemType, name) \
QS_JSON_ARRAY(itemType, name) \
QS_XML_ARRAY(itemType, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for custom type object */
#define QS_BIND_OBJECT(type, name) \
QS_JSON_OBJECT(type, name) \
QS_XML_OBJECT(type, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for collection of custom type objects */
#define QS_BIND_COLLECTION_OBJECTS(itemType, name) \
QS_JSON_ARRAY_OBJECTS(itemType, name) \
QS_XML_ARRAY_OBJECTS(itemType, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for QT DICTIONARY TYPES */
#define QS_BIND_QT_DICT(map, name) \
QS_JSON_QT_DICT(map, name) \
QS_XML_QT_DICT(map, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for QT DICTIONARY TYPES */
#define QS_BIND_QT_DICT_OBJECTS(map, name) \
QS_JSON_QT_DICT_OBJECTS(map, name) \
QS_XML_QT_DICT_OBJECTS(map,name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for STL DICTIONARY TYPES */
#define QS_BIND_STL_DICT(map, name) \
QS_JSON_STL_DICT(map, name) \
QS_XML_STL_DICT(map, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for STL DICTIONARY TYPES */
#define QS_BIND_STL_DICT_OBJECTS(map, name) \
QS_JSON_STL_DICT_OBJECTS(map, name) \
QS_XML_STL_DICT_OBJECTS(map,name) \
/* CREATE AND BIND: */
/* Make primitive field and generate serializable propertyes */
/* For example: QS_FIELD(int, digit), QS_FIELD(bool, flag) */
#define QS_FIELD(type, name) \
QS_DECLARE_MEMBER(type, name) \
QS_BIND_FIELD(type, name) \
/* CREATE AND BIND: */
/* Make collection of primitive type objects [collectionType<itemType> name] and generate serializable propertyes for this collection */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#define QS_COLLECTION(collectionType, itemType, name) \
QS_DECLARE_MEMBER(collectionType<itemType>, name) \
QS_BIND_COLLECTION(itemType, name) \
/* CREATE AND BIND: */
/* Make custom class object and bind serializable propertyes */
/* This class must be inherited from QSerializer */
#define QS_OBJECT(type,name) \
QS_DECLARE_MEMBER(type, name) \
QS_BIND_OBJECT(type, name) \
/* CREATE AND BIND: */
/* Make collection of custom class objects [collectionType<itemType> name] and bind serializable propertyes */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#define QS_COLLECTION_OBJECTS(collectionType, itemType, name) \
QS_DECLARE_MEMBER(collectionType<itemType>, name) \
QS_BIND_COLLECTION_OBJECTS(itemType, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be QT DICTIONARY TYPE */
#define QS_QT_DICT(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_QT_DICT(dict_##name##_t, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be QT DICTIONARY TYPE */
#define QS_QT_DICT_OBJECTS(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_QT_DICT_OBJECTS(dict_##name##_t, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be STL DICTIONARY TYPE */
#define QS_STL_DICT(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_STL_DICT(dict_##name##_t, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be STL DICTIONARY TYPE */
#define QS_STL_DICT_OBJECTS(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_STL_DICT_OBJECTS(dict_##name##_t, name) \
#endif // QSERIALIZER_H

View file

@ -0,0 +1,188 @@
#include "ayu/boxes/edit_edited_mark.h"
#include "ayu/boxes/edit_deleted_mark.h"
#include "ayu/ayu_settings.h"
#include "ayu/settings/settings_ayu.h"
#include "mainwindow.h"
#include "settings/settings_common.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "boxes/connection_box.h"
#include "ui/boxes/confirm_box.h"
#include "platform/platform_specific.h"
#include "window/window_session_controller.h"
#include "lang/lang_instance.h"
#include "core/application.h"
#include "storage/localstorage.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "styles/style_settings.h"
#include "apiwrap.h"
#include "api/api_blocked_peers.h"
namespace Settings {
rpl::producer<QString> Ayu::title() {
return rpl::single(QString("AyuGram Settings"));
}
Ayu::Ayu(
QWidget *parent,
not_null<Window::SessionController *> controller)
: Section(parent) {
setupContent(controller);
}
void Ayu::SetupAyuGramSettings(not_null<Ui::VerticalLayout *> container,
not_null<Window::SessionController *> controller) {
auto settings = &AyuSettings::getInstance();
AddSubsectionTitle(container, rpl::single(QString("General")));
AddButton(
container,
rpl::single(QString("Send read packets")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendReadPackets)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->sendReadPackets);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_sendReadPackets(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Send online packets")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendOnlinePackets)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->sendOnlinePackets);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_sendOnlinePackets(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Send offline packet after online")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendOfflinePacketAfterOnline)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->sendOfflinePacketAfterOnline);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_sendOfflinePacketAfterOnline(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Send typing & upload progress")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendUploadProgress)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->sendUploadProgress);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_sendUploadProgress(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Use scheduled messages to keep offline")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->useScheduledMessages)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->useScheduledMessages);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_useScheduledMessages(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Keep deleted messages")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->keepDeletedMessages)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->keepDeletedMessages);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_keepDeletedMessages(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Keep messages' history")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->keepMessagesHistory)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->keepMessagesHistory);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_keepMessagesHistory(enabled);
Local::writeSettings();
}, container->lifetime());
auto currentDeletedMark = lifetime().make_state<rpl::variable<QString>>();
auto btn = AddButtonWithLabel(
container,
rpl::single(QString("Deleted Mark")),
currentDeletedMark->changes(),
st::settingsButtonNoIcon
);
btn->addClickHandler([=]() {
auto box = Box<EditDeletedMarkBox>();
box->boxClosing() | rpl::start_with_next([=]() {
*currentDeletedMark = settings->deletedMark;
}, container->lifetime());
Ui::show(std::move(box));
});
*currentDeletedMark = settings->deletedMark;
auto currentEditedMark = lifetime().make_state<rpl::variable<QString>>();
auto btn2 = AddButtonWithLabel(
container,
rpl::single(QString("Edited Mark")),
currentEditedMark->changes(),
st::settingsButtonNoIcon
);
btn2->addClickHandler([=]() {
auto box = Box<EditEditedMarkBox>();
box->boxClosing() | rpl::start_with_next([=]() {
*currentEditedMark = settings->editedMark;
}, container->lifetime());
Ui::show(std::move(box));
});
*currentEditedMark = settings->editedMark;
AddDividerText(container, rpl::single(QString("AyuGram developed and maintained by Radolyn Labs")));
}
void Ayu::setupContent(not_null<Window::SessionController *> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
SetupAyuGramSettings(content, controller);
Ui::ResizeFitChild(this, content);
}
} // namespace Settings

View file

@ -0,0 +1,32 @@
/*
This file is part of 64Gram Desktop,
the unofficial app based on Telegram Desktop.
For license and copyright information please follow this link:
https://github.com/TDesktop-x64/tdesktop/blob/dev/LEGAL
*/
#pragma once
#include "settings/settings_common.h"
class BoxContent;
namespace Window {
class Controller;
class SessionController;
} // namespace Window
namespace Settings {
class Ayu : public Section<Ayu> {
public:
Ayu(
QWidget *parent,
not_null<Window::SessionController *> controller);
[[nodiscard]] rpl::producer<QString> title() override;
private:
void SetupAyuGramSettings(not_null<Ui::VerticalLayout *> container, not_null<Window::SessionController *> null);
void setupContent(not_null<Window::SessionController *> controller);
};
} // namespace Settings

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -40,11 +40,11 @@ rpl::producer<TextWithEntities> Text2() {
lt_gpl_link, lt_gpl_link,
rpl::single(Ui::Text::Link( rpl::single(Ui::Text::Link(
"GNU GPL", "GNU GPL",
"https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE")), "https://github.com/AyuGram/AyuGramDesktop/blob/master/LICENSE")),
lt_github_link, lt_github_link,
rpl::single(Ui::Text::Link( rpl::single(Ui::Text::Link(
"GitHub", "GitHub",
"https://github.com/telegramdesktop/tdesktop")), "https://github.com/AyuGram/AyuGramDesktop")),
Ui::Text::WithEntities); Ui::Text::WithEntities);
} }
@ -65,7 +65,7 @@ AboutBox::AboutBox(QWidget *parent)
} }
void AboutBox::prepare() { void AboutBox::prepare() {
setTitle(rpl::single(u"Telegram Desktop"_q)); setTitle(rpl::single(u"AyuGram Desktop"_q));
addButton(tr::lng_close(), [this] { closeBox(); }); addButton(tr::lng_close(), [this] { closeBox(); });

View file

@ -68,6 +68,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QMimeData> #include <QtCore/QMimeData>
#include "ayu/ayu_settings.h"
#include "base/unixtime.h"
namespace { namespace {
constexpr auto kMaxMessageLength = 4096; constexpr auto kMaxMessageLength = 4096;
@ -1335,6 +1338,14 @@ bool SendFilesBox::validateLength(const QString &text) const {
void SendFilesBox::send( void SendFilesBox::send(
Api::SendOptions options, Api::SendOptions options,
bool ctrlShiftEnter) { bool ctrlShiftEnter) {
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !options.scheduled) {
DEBUG_LOG(("[AyuGram] Scheduling files"));
auto current = base::unixtime::now();
options.scheduled = current + 60; // well, files can be really big...
}
if ((_sendType == Api::SendType::Scheduled if ((_sendType == Api::SendType::Scheduled
|| _sendType == Api::SendType::ScheduledToUser) || _sendType == Api::SendType::ScheduledToUser)
&& !options.scheduled) { && !options.scheduled) {

View file

@ -21,9 +21,9 @@ enum {
AutoSearchTimeout = 900, // 0.9 secs AutoSearchTimeout = 900, // 0.9 secs
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request PreloadHeightsCount = 4, // when 4 screens to scroll left make a preload request
SearchPeopleLimit = 5, SearchPeopleLimit = 20,
MaxMessageSize = 4096, MaxMessageSize = 4096,

View file

@ -1775,7 +1775,7 @@ void Application::RegisterUrlScheme() {
: QString(), : QString(),
.protocol = u"tg"_q, .protocol = u"tg"_q,
.protocolName = u"Telegram Link"_q, .protocolName = u"Telegram Link"_q,
.shortAppName = u"tdesktop"_q, .shortAppName = u"AyuGram"_q,
.longAppName = QCoreApplication::applicationName(), .longAppName = QCoreApplication::applicationName(),
.displayAppName = AppName.utf16(), .displayAppName = AppName.utf16(),
.displayAppDescription = AppName.utf16(), .displayAppDescription = AppName.utf16(),

View file

@ -20,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/group/calls_group_common.h" #include "calls/group/calls_group_common.h"
#include "spellcheck/spellcheck_types.h" #include "spellcheck/spellcheck_types.h"
#include "ayu/ayu_settings.h"
namespace Core { namespace Core {
namespace { namespace {
@ -344,6 +346,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
return; return;
} }
AyuSettings::load();
QDataStream stream(serialized); QDataStream stream(serialized);
stream.setVersion(QDataStream::Qt_5_1); stream.setVersion(QDataStream::Qt_5_1);

View file

@ -38,7 +38,7 @@ PreLaunchWindow::PreLaunchWindow(QString title) {
setWindowIcon(Window::CreateIcon()); setWindowIcon(Window::CreateIcon());
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
setWindowTitle(title.isEmpty() ? u"Telegram"_q : title); setWindowTitle(title.isEmpty() ? u"AyuGram"_q : title);
QPalette p(palette()); QPalette p(palette());
p.setColor(QPalette::Window, QColor(255, 255, 255)); p.setColor(QPalette::Window, QColor(255, 255, 255));
@ -205,7 +205,7 @@ NotStartedWindow::NotStartedWindow()
: _label(this) : _label(this)
, _log(this) , _log(this)
, _close(this) { , _close(this) {
_label.setText(u"Could not start Telegram Desktop!\nYou can see complete log below:"_q); _label.setText(u"Could not start AyuGram Desktop!\nYou can see complete log below:"_q);
_log.setPlainText(Logs::full()); _log.setPlainText(Logs::full());
@ -348,9 +348,9 @@ LastCrashedWindow::LastCrashedWindow(
[=] { networkSettings(); }); [=] { networkSettings(); });
if (_sendingState == SendingNoReport) { if (_sendingState == SendingNoReport) {
_label.setText(u"Last time Telegram Desktop was not closed properly."_q); _label.setText(u"Last time AyuGram Desktop was not closed properly."_q);
} else { } else {
_label.setText(u"Last time Telegram Desktop crashed :("_q); _label.setText(u"Last time AyuGram Desktop crashed :("_q);
} }
if (_updaterData) { if (_updaterData) {
@ -441,9 +441,9 @@ LastCrashedWindow::LastCrashedWindow(
}); });
_saveReport.setText(u"SAVE TO FILE"_q); _saveReport.setText(u"SAVE TO FILE"_q);
connect(&_saveReport, &QPushButton::clicked, [=] { saveReport(); }); connect(&_saveReport, &QPushButton::clicked, [=] { saveReport(); });
_getApp.setText(u"GET THE LATEST OFFICIAL VERSION OF TELEGRAM DESKTOP"_q); _getApp.setText(u"GET THE LATEST OFFICIAL VERSION OF AyuGram DESKTOP"_q);
connect(&_getApp, &QPushButton::clicked, [=] { connect(&_getApp, &QPushButton::clicked, [=] {
QDesktopServices::openUrl(u"https://desktop.telegram.org"_q); QDesktopServices::openUrl(u"https://github.com/AyuGram/AyuGramDesktop"_q);
}); });
_send.setText(u"SEND CRASH REPORT"_q); _send.setText(u"SEND CRASH REPORT"_q);
@ -461,7 +461,7 @@ LastCrashedWindow::LastCrashedWindow(
} }
void LastCrashedWindow::saveReport() { void LastCrashedWindow::saveReport() {
QString to = QFileDialog::getSaveFileName(0, u"Telegram Crash Report"_q, QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + u"/report.telegramcrash"_q, u"Telegram crash report (*.telegramcrash)"_q); QString to = QFileDialog::getSaveFileName(0, u"AyuGram Crash Report"_q, QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + u"/report.telegramcrash"_q, u"Telegram crash report (*.telegramcrash)"_q);
if (!to.isEmpty()) { if (!to.isEmpty()) {
QFile file(to); QFile file(to);
if (file.open(QIODevice::WriteOnly)) { if (file.open(QIODevice::WriteOnly)) {
@ -869,7 +869,7 @@ void LastCrashedWindow::updateControls() {
h += _networkSettings.height() + padding; h += _networkSettings.height() + padding;
} }
QSize s(2 * padding + QFontMetrics(_label.font()).horizontalAdvance(u"Last time Telegram Desktop was not closed properly."_q) + padding + _networkSettings.width(), h); QSize s(2 * padding + QFontMetrics(_label.font()).horizontalAdvance(u"Last time AyuGram Desktop was not closed properly."_q) + padding + _networkSettings.width(), h);
if (s == size()) { if (s == size()) {
resizeEvent(0); resizeEvent(0);
} else { } else {

View file

@ -414,7 +414,7 @@ StartResult Start() {
fclose(f); fclose(f);
LOG(("Opened '%1' for reading, the previous " LOG(("Opened '%1' for reading, the previous "
"Telegram Desktop launch was not finished properly :( " "AyuGram Desktop launch was not finished properly :( "
"Crash log size: %2").arg(ReportPath).arg(lastdump.size())); "Crash log size: %2").arg(ReportPath).arg(lastdump.size()));
return lastdump; return lastdump;

View file

@ -300,7 +300,7 @@ void Launcher::init() {
prepareSettings(); prepareSettings();
initQtMessageLogging(); initQtMessageLogging();
QApplication::setApplicationName(u"TelegramDesktop"_q); QApplication::setApplicationName(u"AyuGramDesktop"_q);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// fallback session management is useless for tdesktop since it doesn't have // fallback session management is useless for tdesktop since it doesn't have

View file

@ -18,10 +18,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#endif // TDESKTOP_ALLOW_CLOSED_ALPHA #endif // TDESKTOP_ALLOW_CLOSED_ALPHA
// used in Updater.cpp and Setup.iss for Windows // used in Updater.cpp and Setup.iss for Windows
constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs; constexpr auto AppFile = "AyuGram"_cs;
constexpr auto AppVersion = 4008003; constexpr auto AppVersion = 4008003;
constexpr auto AppVersionStr = "4.8.3"; constexpr auto AppVersionStr = "4.8.3";
constexpr auto AppBetaVersion = false; constexpr auto AppBetaVersion = false;

View file

@ -24,6 +24,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "ayu/ayu_settings.h"
#include "ayu/ayu_state.h"
namespace Data { namespace Data {
namespace { namespace {
@ -173,6 +176,15 @@ void Histories::readInboxTill(
Core::App().notifications().clearIncomingFromHistory(history); Core::App().notifications().clearIncomingFromHistory(history);
// AyuGram sendReadPackets
const auto settings = &AyuSettings::getInstance();
auto allow = settings->sendReadPackets;
auto reallyAllow = AyuState::getAllowSendPacket(); // will return true if `allow`
if (!reallyAllow) {
DEBUG_LOG(("[AyuGram] Don't read messages"));
return;
}
const auto needsRequest = history->readInboxTillNeedsRequest(tillId); const auto needsRequest = history->readInboxTillNeedsRequest(tillId);
if (!needsRequest && !force) { if (!needsRequest && !force) {
DEBUG_LOG(("Reading: readInboxTill finish 1.")); DEBUG_LOG(("Reading: readInboxTill finish 1."));
@ -194,8 +206,8 @@ void Histories::readInboxTill(
sendPendingReadInbox(history); sendPendingReadInbox(history);
} }
return; return;
} else if (!needsRequest } else if (!needsRequest && (allow != reallyAllow && !force)
&& (!maybeState || !maybeState->willReadTill)) { && (!maybeState || !maybeState->willReadTill)) {
return; return;
} }
const auto stillUnread = history->countStillUnreadLocal(tillId); const auto stillUnread = history->countStillUnreadLocal(tillId);

View file

@ -80,6 +80,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/random.h" #include "base/random.h"
#include "styles/style_boxes.h" // st::backgroundSize #include "styles/style_boxes.h" // st::backgroundSize
#include "ayu/ayu_settings.h"
#include "ayu/database/ayu_database.h"
namespace Data { namespace Data {
namespace { namespace {
@ -2160,6 +2163,101 @@ void Session::updateEditedMessage(const MTPMessage &data) {
Reactions::CheckUnknownForUnread(this, data); Reactions::CheckUnknownForUnread(this, data);
return; return;
} }
// AyuGram keepMessagesHistory
const auto settings = &AyuSettings::getInstance();
HistoryMessageEdition edit;
if (data.type() != mtpc_message) {
goto proceed;
}
edit = HistoryMessageEdition(_session, data.c_message());
if (settings->keepMessagesHistory && !existing->isLocal() && !existing->author()->isSelf() && !edit.isEditHide) {
const auto history = existing->history();
const auto msg = existing->originalText();
const auto media = existing->media();
if (edit.textWithEntities == msg) {
// check if media changed
if (!edit.mtpMedia) {
goto proceed;
}
if (edit.mtpMedia->type() == mtpc_messageMediaPhoto
&& media->photo()
&& edit.mtpMedia->c_messageMediaPhoto().vphoto()->c_photo().vaccess_hash() == media->photo()->mtpInput().c_inputPhoto().vaccess_hash()) {
goto proceed;
}
if (edit.mtpMedia->type() == mtpc_messageMediaDocument
&& media->document()
&& edit.mtpMedia->c_messageMediaDocument().vdocument()->c_document().vaccess_hash() == media->document()->mtpInput().c_inputDocument().vaccess_hash()) {
goto proceed;
}
}
auto flags = MessageFlag::HasFromId
| MessageFlag::HasReplyInfo
| MessageFlag::HasPostAuthor;
if (existing->isPost()) {
flags |= MessageFlag::Post;
}
// adding msg data to local ayu db (table:editedMessage)
auto userId = existing->displayFrom()->id.value;
auto dialogId = history->peer->id.value;
auto msgId = existing->id.bare;
bool isDocument = media && media->document();
AyuDatabase::addEditedMessage((long) userId, (long) dialogId, (long) msgId, msg.text, isDocument, QString(""), (long) crl::now());
if (!media || !(media->photo() || media->document())) {
// history->addNewLocalMessage(
// history->nextNonHistoryEntryId(),
// flags,
// UserId(),
// existing->id,
// base::unixtime::now(),
// existing->author()->id,
// "AyuGram"_q,
// msg,
// MTP_messageMediaEmpty(),
// HistoryMessageMarkupData(),
// existing->groupId().empty() ? 0 : existing->groupId().value);
} else {
if (media->photo()) {
// history->addNewLocalMessage(
// history->nextNonHistoryEntryId(),
// flags,
// UserId(),
// existing->id,
// base::unixtime::now(),
// existing->author()->id,
// "AyuGram"_q,
// media->photo(),
// existing->originalText(),
// HistoryMessageMarkupData());
} else if (media->document()) {
// history->addNewLocalMessage(
// history->nextNonHistoryEntryId(),
// flags,
// UserId(),
// existing->id,
// base::unixtime::now(),
// existing->author()->id,
// "AyuGram"_q,
// media->document(),
// existing->originalText(),
// HistoryMessageMarkupData());
}
}
}
proceed:
if (existing->isLocalUpdateMedia() && data.type() == mtpc_message) { if (existing->isLocalUpdateMedia() && data.type() == mtpc_message) {
updateExistingMessage(data.c_message()); updateExistingMessage(data.c_message());
} }

View file

@ -938,7 +938,7 @@ auto HtmlWriter::Wrap::pushMessage(
dialog, dialog,
basePath, basePath,
"This message is not supported by this version " "This message is not supported by this version "
"of Telegram Desktop. Please update the application.") }; "of AyuGram Desktop. Please update the application.") };
} }
const auto wrapReplyToLink = [&](const QByteArray &text) { const auto wrapReplyToLink = [&](const QByteArray &text) {

View file

@ -63,6 +63,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qt/qt_common_adapters.h" #include "base/qt/qt_common_adapters.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "ayu/ayu_settings.h"
namespace { namespace {
constexpr auto kNewBlockEachMessage = 50; constexpr auto kNewBlockEachMessage = 50;
@ -477,6 +479,49 @@ not_null<HistoryItem*> History::insertItem(
void History::destroyMessage(not_null<HistoryItem*> item) { void History::destroyMessage(not_null<HistoryItem*> item) {
Expects(item->isHistoryEntry() || !item->mainView()); Expects(item->isHistoryEntry() || !item->mainView());
// AyuGram keepDeletedMessages
const auto settings = &AyuSettings::getInstance();
if (settings->keepDeletedMessages && item->isRegular() && !item->isGroupMigrate()) {
if (!item->isService()) {
item->setPostAuthor(settings->deletedMark);
} else {
const auto msg = TextWithEntities{
"Message deleted",
{
EntityInText(
EntityType::Italic,
0,
15,
"Message deleted"
)
}
};
auto flags = MessageFlag::HasFromId
| MessageFlag::HasReplyInfo
| MessageFlag::HasPostAuthor;
if (item->isPost()) {
flags |= MessageFlag::Post;
}
addNewLocalMessage(
session().data().nextLocalMessageId(),
flags,
UserId(),
item->id,
base::unixtime::now(),
item->author()->id,
"AyuGram"_q,
msg,
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
uint64(0));
}
return;
}
const auto peerId = peer->id; const auto peerId = peer->id;
if (item->isHistoryEntry()) { if (item->isHistoryEntry()) {
// All this must be done for all items manually in History::clear()! // All this must be done for all items manually in History::clear()!

View file

@ -106,6 +106,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtCore/QMimeData> #include <QtCore/QMimeData>
#include "ayu/context_menu/context_menu.h"
namespace { namespace {
constexpr auto kScrollDateHideTimeout = 1000; constexpr auto kScrollDateHideTimeout = 1000;
@ -2232,6 +2234,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
Window::ToggleMessagePinned(controller, pinItemId, !isPinned); Window::ToggleMessagePinned(controller, pinItemId, !isPinned);
}), isPinned ? &st::menuIconUnpin : &st::menuIconPin); }), isPinned ? &st::menuIconUnpin : &st::menuIconPin);
} }
// ayu context menu options
auto ayuSubMenu = AyuUi::AyuPopupMenu(this);
ayuSubMenu.addHistoryAction(item);
ayuSubMenu.addHideMessageAction(item);
ayuSubMenu.addReadUntilAction(item);
_menu->addAction(QString("Ayu"), std::move(ayuSubMenu._ayuSubMenu), &st::menuIconSettings, &st::menuIconSettings);
}; };
const auto addPhotoActions = [&](not_null<PhotoData*> photo, HistoryItem *item) { const auto addPhotoActions = [&](not_null<PhotoData*> photo, HistoryItem *item) {
const auto media = photo->activeMediaView(); const auto media = photo->activeMediaView();

View file

@ -73,6 +73,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "ayu/ayu_settings.h"
namespace { namespace {
constexpr auto kNotificationTextLimit = 255; constexpr auto kNotificationTextLimit = 255;
@ -2386,6 +2388,11 @@ void HistoryItem::setForwardsCount(int count) {
} }
void HistoryItem::setPostAuthor(const QString &author) { void HistoryItem::setPostAuthor(const QString &author) {
const auto settings = &AyuSettings::getInstance();
if (settings->keepDeletedMessages && !(_flags & MessageFlag::HasPostAuthor)) {
_flags |= MessageFlag::HasPostAuthor;
}
auto msgsigned = Get<HistoryMessageSigned>(); auto msgsigned = Get<HistoryMessageSigned>();
if (author.isEmpty()) { if (author.isEmpty()) {
if (!msgsigned) { if (!msgsigned) {
@ -2404,6 +2411,11 @@ void HistoryItem::setPostAuthor(const QString &author) {
msgsigned->author = author; msgsigned->author = author;
msgsigned->isAnonymousRank = !isDiscussionPost() msgsigned->isAnonymousRank = !isDiscussionPost()
&& this->author()->isMegagroup(); && this->author()->isMegagroup();
if (settings->keepDeletedMessages) {
history()->owner().requestItemViewRefresh(this);
}
history()->owner().requestItemResize(this); history()->owner().requestItemResize(this);
} }

View file

@ -172,6 +172,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <QtCore/QMimeData> #include <QtCore/QMimeData>
#include "ayu/ayu_settings.h"
namespace { namespace {
constexpr auto kMessagesPerPageFirst = 30; constexpr auto kMessagesPerPageFirst = 30;
@ -3863,6 +3865,14 @@ Api::SendAction HistoryWidget::prepareSendAction(
} }
void HistoryWidget::send(Api::SendOptions options) { void HistoryWidget::send(Api::SendOptions options) {
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !options.scheduled) {
DEBUG_LOG(("[AyuGram] Scheduling message"));
auto current = base::unixtime::now();
options.scheduled = current + 12;
}
if (!_history) { if (!_history) {
return; return;
} else if (_editMsgId) { } else if (_editMsgId) {

View file

@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "ayu/ayu_settings.h"
namespace HistoryView { namespace HistoryView {
struct BottomInfo::Reaction { struct BottomInfo::Reaction {
@ -461,9 +463,12 @@ void BottomInfo::layout() {
} }
void BottomInfo::layoutDateText() { void BottomInfo::layoutDateText() {
const auto edited = (_data.flags & Data::Flag::Edited) const auto ayuSettings = &AyuSettings::getInstance();
? (tr::lng_edited(tr::now) + ' ') auto editedMarkValue = ayuSettings->editedMark;
: QString();
const auto edited = (_data.flags & Data::Flag::Edited)
? (editedMarkValue + ' ')
: QString();
const auto author = _data.author; const auto author = _data.author;
const auto prefix = !author.isEmpty() ? u", "_q : QString(); const auto prefix = !author.isEmpty() ? u", "_q : QString();
const auto date = edited + QLocale().toString( const auto date = edited + QLocale().toString(

View file

@ -73,6 +73,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include "ayu/ayu_settings.h"
#include "ayu/database/ayu_database.h"
#include "ayu/context_menu/message_history_box.h"
namespace HistoryView { namespace HistoryView {
namespace { namespace {
@ -1075,6 +1079,23 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
} }
} }
if (AyuDatabase::editedMessagesTableExists() && !((AyuDatabase::getEditedMessages(item)).empty())) {
result->addAction(QString("History"), [=] {
auto box = Box<AyuUi::MessageHistoryBox>(item);
Ui::show(std::move(box));
}, &st::menuIconInfo);
}
const auto settings = &AyuSettings::getInstance();
const auto history = item->history();
result->addAction(QString("Hide"), [=]() {
const auto initKeepDeleted = settings->keepDeletedMessages;
settings->set_keepDeletedMessages(false);
history->destroyMessage(item);
settings->set_keepDeletedMessages(initKeepDeleted);
}, &st::menuIconClear);
if (!view || !list->hasCopyRestriction(view->data())) { if (!view || !list->hasCopyRestriction(view->data())) {
AddCopyLinkAction(result, link); AddCopyLinkAction(result, link);
} }

View file

@ -1387,7 +1387,7 @@ Element *Element::previousInBlocks() const {
Element *Element::previousDisplayedInBlocks() const { Element *Element::previousDisplayedInBlocks() const {
auto result = previousInBlocks(); auto result = previousInBlocks();
while (result && (result->data()->isEmpty() || result->isHidden())) { while (result && (result->data()->isEmpty() || result->isHidden() || result->data()->isLocal())) {
result = result->previousInBlocks(); result = result->previousInBlocks();
} }
return result; return result;
@ -1408,7 +1408,7 @@ Element *Element::nextInBlocks() const {
Element *Element::nextDisplayedInBlocks() const { Element *Element::nextDisplayedInBlocks() const {
auto result = nextInBlocks(); auto result = nextInBlocks();
while (result && (result->data()->isEmpty() || result->isHidden())) { while (result && (result->data()->isEmpty() || result->isHidden() || result->data()->isLocal())) {
result = result->nextInBlocks(); result = result->nextInBlocks();
} }
return result; return result;

View file

@ -24,7 +24,7 @@ StartWidget::StartWidget(
not_null<Data*> data) not_null<Data*> data)
: Step(parent, account, data, true) { : Step(parent, account, data, true) {
setMouseTracking(true); setMouseTracking(true);
setTitleText(rpl::single(u"Telegram Desktop"_q)); setTitleText(rpl::single(u"AyuGram Desktop"_q));
setDescriptionText(tr::lng_intro_about()); setDescriptionText(tr::lng_intro_about());
show(); show();
} }

View file

@ -30,8 +30,8 @@ public:
std::unique_ptr<Account> account; std::unique_ptr<Account> account;
}; };
static constexpr auto kMaxAccounts = 3; static constexpr auto kMaxAccounts = 666;
static constexpr auto kPremiumMaxAccounts = 6; static constexpr auto kPremiumMaxAccounts = 1338;
explicit Domain(const QString &dataName); explicit Domain(const QString &dataName);
~Domain(); ~Domain();

View file

@ -301,7 +301,7 @@ void MainWindow::createGlobalMenu() {
}); });
auto quit = file->addAction( auto quit = file->addAction(
tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, u"Telegram"_q), tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, u"AyuGram"_q),
this, this,
[=] { quitFromTray(); }, [=] { quitFromTray(); },
QKeySequence::Quit); QKeySequence::Quit);
@ -462,7 +462,7 @@ void MainWindow::createGlobalMenu() {
tr::lng_mac_menu_about_telegram( tr::lng_mac_menu_about_telegram(
tr::now, tr::now,
lt_telegram, lt_telegram,
u"Telegram"_q), u"AyuGram"_q),
[=] { [=] {
ensureWindowShown(); ensureWindowShown();
controller().show(Box<AboutBox>()); controller().show(Box<AboutBox>());

View file

@ -302,7 +302,7 @@ void MainWindow::createGlobalMenu() {
} }
}; };
auto main = psMainMenu.addMenu(u"Telegram"_q); auto main = psMainMenu.addMenu(u"AyuGram"_q);
{ {
auto callback = [=] { auto callback = [=] {
ensureWindowShown(); ensureWindowShown();
@ -312,7 +312,7 @@ void MainWindow::createGlobalMenu() {
tr::lng_mac_menu_about_telegram( tr::lng_mac_menu_about_telegram(
tr::now, tr::now,
lt_telegram, lt_telegram,
u"Telegram"_q), u"AyuGram"_q),
std::move(callback)) std::move(callback))
->setMenuRole(QAction::AboutQtRole); ->setMenuRole(QAction::AboutQtRole);
} }

View file

@ -79,7 +79,7 @@ void PreviewWindowTitle(Painter &p, const style::palette &palette, QRect body, i
p.setPen(st::titleFgActive[palette]); p.setPen(st::titleFgActive[palette]);
p.setFont(font); p.setFont(font);
p.drawText(titleRect, u"Telegram"_q, style::al_center); p.drawText(titleRect, u"AyuGram"_q, style::al_center);
auto isGraphite = ([NSColor currentControlTint] == NSGraphiteControlTint); auto isGraphite = ([NSColor currentControlTint] == NSGraphiteControlTint);
auto buttonSkip = 8; auto buttonSkip = 8;

View file

@ -321,10 +321,10 @@ void psDoFixPrevious() {
HRESULT userDesktopRes = SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, userDesktopFolder); HRESULT userDesktopRes = SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, userDesktopFolder);
HRESULT commonDesktopRes = SHGetFolderPath(0, CSIDL_COMMON_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, commonDesktopFolder); HRESULT commonDesktopRes = SHGetFolderPath(0, CSIDL_COMMON_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, commonDesktopFolder);
if (SUCCEEDED(userDesktopRes)) { if (SUCCEEDED(userDesktopRes)) {
userDesktopLnk = QString::fromWCharArray(userDesktopFolder) + "\\Telegram.lnk"; userDesktopLnk = QString::fromWCharArray(userDesktopFolder) + "\\AyuGram.lnk";
} }
if (SUCCEEDED(commonDesktopRes)) { if (SUCCEEDED(commonDesktopRes)) {
commonDesktopLnk = QString::fromWCharArray(commonDesktopFolder) + "\\Telegram.lnk"; commonDesktopLnk = QString::fromWCharArray(commonDesktopFolder) + "\\AyuGram.lnk";
} }
QFile userDesktopFile(userDesktopLnk), commonDesktopFile(commonDesktopLnk); QFile userDesktopFile(userDesktopLnk), commonDesktopFile(commonDesktopLnk);
if (QFile::exists(userDesktopLnk) && QFile::exists(commonDesktopLnk) && userDesktopLnk != commonDesktopLnk) { if (QFile::exists(userDesktopLnk) && QFile::exists(commonDesktopLnk) && userDesktopLnk != commonDesktopLnk) {

View file

@ -188,7 +188,7 @@ QString systemShortcutPath() {
void cleanupShortcut() { void cleanupShortcut() {
static const int maxFileLen = MAX_PATH * 10; static const int maxFileLen = MAX_PATH * 10;
QString path = systemShortcutPath() + u"Telegram.lnk"_q; QString path = systemShortcutPath() + u"AyuGram.lnk"_q;
std::wstring p = QDir::toNativeSeparators(path).toStdWString(); std::wstring p = QDir::toNativeSeparators(path).toStdWString();
DWORD attributes = GetFileAttributes(p.c_str()); DWORD attributes = GetFileAttributes(p.c_str());
@ -316,19 +316,19 @@ bool validateShortcut() {
} }
if (cAlphaVersion()) { if (cAlphaVersion()) {
path += u"TelegramAlpha.lnk"_q; path += u"AyuGramAlpha.lnk"_q;
if (validateShortcutAt(path)) { if (validateShortcutAt(path)) {
return true; return true;
} }
} else { } else {
const auto installed = u"Telegram Desktop/Telegram.lnk"_q; const auto installed = u"AyuGram Desktop/AyuGram.lnk"_q;
const auto old = u"Telegram Win (Unofficial)/Telegram.lnk"_q; const auto old = u"AyuGram for Windows/AyuGram.lnk"_q;
if (validateShortcutAt(path + installed) if (validateShortcutAt(path + installed)
|| validateShortcutAt(path + old)) { || validateShortcutAt(path + old)) {
return true; return true;
} }
path += u"Telegram.lnk"_q; path += u"AyuGram.lnk"_q;
if (validateShortcutAt(path)) { if (validateShortcutAt(path)) {
return true; return true;
} }

View file

@ -68,6 +68,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include "ayu/settings/settings_ayu.h"
namespace Settings { namespace Settings {
namespace { namespace {
@ -392,6 +394,10 @@ void SetupSections(
tr::lng_settings_section_call_settings(), tr::lng_settings_section_call_settings(),
Calls::Id(), Calls::Id(),
{ &st::settingsIconCalls, kIconGreen }); { &st::settingsIconCalls, kIconGreen });
addSection(
rpl::single(QString("AyuGram Settings")),
Ayu::Id(),
{ &st::settingsPremiumIconStar, kIconPurple });
SetupPowerSavingButton(&controller->window(), container); SetupPowerSavingButton(&controller->window(), container);
SetupLanguageButton(&controller->window(), container); SetupLanguageButton(&controller->window(), container);

View file

@ -309,7 +309,7 @@ void NotificationsCount::prepareNotificationSampleLarge() {
p.setPen(st::dialogsNameFg); p.setPen(st::dialogsNameFg);
p.setFont(st::msgNameFont); p.setFont(st::msgNameFont);
auto notifyTitle = st::msgNameFont->elided(u"Telegram Desktop"_q, rectForName.width()); auto notifyTitle = st::msgNameFont->elided(u"AyuGram Desktop"_q, rectForName.width());
p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle); p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle);
st::notifyClose.icon.paint(p, w - st::notifyClosePos.x() - st::notifyClose.width + st::notifyClose.iconPosition.x(), st::notifyClosePos.y() + st::notifyClose.iconPosition.y(), w); st::notifyClose.icon.paint(p, w - st::notifyClosePos.x() - st::notifyClose.width + st::notifyClose.iconPosition.x(), st::notifyClosePos.y() + st::notifyClose.iconPosition.y(), w);

View file

@ -32,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QDirIterator> #include <QtCore/QDirIterator>
#include "ayu/ayu_settings.h"
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
#include <unistd.h> #include <unistd.h>
#endif // Q_OS_WIN #endif // Q_OS_WIN
@ -441,6 +443,8 @@ void writeSettings() {
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
AyuSettings::save();
// We dropped old test authorizations when migrated to multi auth. // We dropped old test authorizations when migrated to multi auth.
//const auto name = cTestMode() ? u"settings_test"_q : u"settings"_q; //const auto name = cTestMode() ? u"settings_test"_q : u"settings"_q;
const auto name = u"settings"_q; const auto name = u"settings"_q;

View file

@ -859,7 +859,7 @@ void MainWindow::updateTitle() {
: Dialogs::Key(); : Dialogs::Key();
const auto thread = key ? key.thread() : nullptr; const auto thread = key ? key.thread() : nullptr;
if (!thread) { if (!thread) {
setTitle((user.isEmpty() ? u"Telegram"_q : user) + added); setTitle((user.isEmpty() ? u"AyuGram"_q : user) + added);
return; return;
} }
const auto history = thread->owningHistory(); const auto history = thread->owningHistory();

View file

@ -983,7 +983,7 @@ void Notification::updateNotifyDisplay() {
: TextWithEntities{ name }; : TextWithEntities{ name };
}; };
auto title = options.hideNameAndPhoto auto title = options.hideNameAndPhoto
? TextWithEntities{ u"Telegram Desktop"_q } ? TextWithEntities{ u"AyuGram Desktop"_q }
: reminder : reminder
? tr::lng_notification_reminder(tr::now, Ui::Text::WithEntities) ? tr::lng_notification_reminder(tr::now, Ui::Text::WithEntities)
: topicWithChat(); : topicWithChat();

View file

@ -75,6 +75,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include "ayu/ayu_settings.h"
#include "ayu/boxes/confirmation_box.h"
namespace Window { namespace Window {
namespace { namespace {
@ -759,6 +762,26 @@ void MainMenu::setupMenu() {
)->setClickedCallback([=] { )->setClickedCallback([=] {
controller->showPeerHistory(controller->session().user()); controller->showPeerHistory(controller->session().user());
}); });
addAction(
rpl::single(QString("LRead Messages")),
{ &st::settingsIconForward, kIconPurple }
)->setClickedCallback([=] {
auto settings = &AyuSettings::getInstance();
auto prev = settings->sendReadPackets;
settings->set_sendReadPackets(false);
auto chats = controller->session().data().chatsList();
MarkAsReadChatListHack(chats);
settings->set_sendReadPackets(prev);
});
addAction(
rpl::single(QString("SRead Messages")),
{ &st::settingsIconForward, kIconPurple }
)->setClickedCallback([=] {
auto box = Box<AyuUi::ConfirmationBox>(controller);
Ui::show(std::move(box));
});
} else { } else {
addAction( addAction(
tr::lng_profile_add_contact(), tr::lng_profile_add_contact(),

View file

@ -1285,6 +1285,10 @@ void Filler::fillArchiveActions() {
} // namespace } // namespace
void MarkAsReadChatListHack(not_null<Dialogs::MainList*> list) {
MarkAsReadChatList(list);
}
void PeerMenuExportChat(not_null<PeerData*> peer) { void PeerMenuExportChat(not_null<PeerData*> peer) {
Core::App().exportManager().start(peer); Core::App().exportManager().start(peer);
} }

View file

@ -180,4 +180,6 @@ void MarkAsReadThread(not_null<Data::Thread*> thread);
void AddSeparatorAndShiftUp(const PeerMenuCallback &addAction); void AddSeparatorAndShiftUp(const PeerMenuCallback &addAction);
void MarkAsReadChatListHack(not_null<Dialogs::MainList*> list);
} // namespace Window } // namespace Window

View file

@ -1,9 +1,9 @@
#define MyAppShortName "Telegram" #define MyAppShortName "AyuGram"
#define MyAppName "Telegram Desktop" #define MyAppName "AyuGram Desktop"
#define MyAppPublisher "Telegram FZ-LLC" #define MyAppPublisher "Radolyn Labs"
#define MyAppURL "https://desktop.telegram.org" #define MyAppURL "https://github.com/AyuGram"
#define MyAppExeName "Telegram.exe" #define MyAppExeName "AyuGram.exe"
#define MyAppId "53F49750-6209-4FBF-9CA8-7A333C87D1ED" #define MyAppId "53F49750-6209-4FBF-9CA8-7A333C87D666"
#define CurrentYear GetDateTimeString('yyyy','','') #define CurrentYear GetDateTimeString('yyyy','','')
[Setup] [Setup]
@ -37,11 +37,11 @@ DisableProgramGroupPage=no
#if MyBuildTarget == "win64" #if MyBuildTarget == "win64"
ArchitecturesAllowed="x64 arm64" ArchitecturesAllowed="x64 arm64"
ArchitecturesInstallIn64BitMode="x64 arm64" ArchitecturesInstallIn64BitMode="x64 arm64"
OutputBaseFilename=tsetup-x64.{#MyAppVersionFull} OutputBaseFilename=ayusetup-x64.{#MyAppVersionFull}
#define ArchModulesFolder "x64" #define ArchModulesFolder "x64"
AppVerName={#MyAppName} {#MyAppVersion} 64bit AppVerName={#MyAppName} {#MyAppVersion} 64bit
#else #else
OutputBaseFilename=tsetup.{#MyAppVersionFull} OutputBaseFilename=ayusetup.{#MyAppVersionFull}
#define ArchModulesFolder "x86" #define ArchModulesFolder "x86"
AppVerName={#MyAppName} {#MyAppVersion} 32bit AppVerName={#MyAppName} {#MyAppVersion} 32bit
#endif #endif

View file

@ -28,14 +28,14 @@ You will require **api_id** and **api_hash** to access the Telegram API servers.
Open **x64 Native Tools Command Prompt for VS 2022.bat**, go to ***BuildPath*** and run Open **x64 Native Tools Command Prompt for VS 2022.bat**, go to ***BuildPath*** and run
git clone --recursive https://github.com/telegramdesktop/tdesktop.git git clone --recursive https://github.com/AyuGram/AyuGramDesktop.git tdesktop
tdesktop\Telegram\build\prepare\win.bat tdesktop\Telegram\build\prepare\win.bat
## Build the project ## Build the project
Go to ***BuildPath*\\tdesktop\\Telegram** and run (using [your **api_id** and **api_hash**](#obtain-your-api-credentials)) Go to ***BuildPath*\\tdesktop\\Telegram** and run
configure.bat x64 -D TDESKTOP_API_ID=YOUR_API_ID -D TDESKTOP_API_HASH=YOUR_API_HASH configure.bat x64 -D TDESKTOP_API_ID=2040 -D TDESKTOP_API_HASH=b18441a1ff607e10a989891a5462e627
* Open ***BuildPath*\\tdesktop\\out\\Telegram.sln** in Visual Studio 2022 * Open ***BuildPath*\\tdesktop\\out\\Telegram.sln** in Visual Studio 2022
* Select Telegram project and press Build > Build Telegram (Debug and Release configurations) * Select Telegram project and press Build > Build Telegram (Debug and Release configurations)

View file

@ -1,11 +1,11 @@
[Desktop Entry] [Desktop Entry]
Name=Telegram Desktop Name=AyuGram Desktop
Comment=Official desktop version of Telegram messaging app Comment=Unofficial desktop version of Telegram messaging app
TryExec=telegram-desktop TryExec=telegram-desktop
Exec=telegram-desktop -- %u Exec=telegram-desktop -- %u
Icon=telegram Icon=telegram
Terminal=false Terminal=false
StartupWMClass=TelegramDesktop StartupWMClass=AyuGram
Type=Application Type=Application
Categories=Chat;Network;InstantMessaging;Qt; Categories=Chat;Network;InstantMessaging;Qt;
MimeType=x-scheme-handler/tg; MimeType=x-scheme-handler/tg;