feat: message details

feat: rework `Hide`
feat: context menu configuration
This commit is contained in:
ZavaruKitsu 2023-12-29 01:22:07 +03:00
parent bf743836c1
commit 8d93b21947
22 changed files with 1611 additions and 351 deletions

View file

@ -107,6 +107,7 @@ PRIVATE
ayu/ayu_constants.h
ayu/utils/ayu_mapper.cpp
ayu/utils/ayu_mapper.h
ayu/utils/qt_key_modifiers_extended.h
ayu/utils/telegram_helpers.cpp
ayu/utils/telegram_helpers.h
ayu/utils/windows_utils.cpp
@ -123,6 +124,8 @@ PRIVATE
ayu/ui/settings/settings_ayu.h
ayu/ui/context_menu/context_menu.cpp
ayu/ui/context_menu/context_menu.h
ayu/ui/context_menu/menu_item_subtext.cpp
ayu/ui/context_menu/menu_item_subtext.h
ayu/ui/sections/edited/edited_log_inner.cpp
ayu/ui/sections/edited/edited_log_inner.h
ayu/ui/sections/edited/edited_log_item.cpp

View file

@ -4633,6 +4633,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ayu_SettingsShowID_Hide" = "Hide";
"ayu_SettingsRecentStickersCount" = "Recent stickers count";
"ayu_SettingsCustomizationHint" = "After making changes to the \"Customization\" section, you must restart the application.";
"ayu_SettingsContextMenuTitle" = "Select item show type";
"ayu_SettingsContextMenuDescription" = "Extended menu items will be displayed if you hold CTRL or SHIFT while right-clicking on the message.";
"ayu_SettingsContextMenuItemHidden" = "Hidden";
"ayu_SettingsContextMenuItemShown" = "Shown";
"ayu_SettingsContextMenuItemExtended" = "Extended menu";
"ayu_SettingsContextMenuReactionsPanel" = "Reactions Panel";
"ayu_SettingsContextMenuViewsPanel" = "Views Panel";
"ayu_ContextMenuElementsHeader" = "Context Menu Elements";
"ayu_DrawerElementsHeader" = "Drawer Elements";
"ayu_TrayElementsHeader" = "Tray Elements";
"ayu_RegexFilters" = "Message Filters";
@ -4690,6 +4698,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ayu_StickerConfirmation" = "For stickers";
"ayu_GIFConfirmation" = "For GIFs";
"ayu_VoiceConfirmation" = "For voice messages";
"ayu_MessageDetailsPC" = "Details";
"ayu_MessageDetailsViewsPC" = "Views";
"ayu_MessageDetailsSharesPC" = "Shares";
"ayu_MessageDetailsDatePC" = "Date";
"ayu_MessageDetailsEditedDatePC" = "Edit Date";
"ayu_MessageDetailsForwardedDatePC" = "Forward Date";
"ayu_MessageDetailsFileSizePC" = "File Size";
"ayu_MessageDetailsFileNamePC" = "File Name";
"ayu_MessageDetailsFilePathPC" = "File Path";
"ayu_MessageDetailsResolutionPC" = "Resolution";
"ayu_MessageDetailsBitratePC" = "Bitrate";
"ayu_MessageDetailsMimeTypePC" = "Mime Type";
"ayu_MessageDetailsDatacenterPC" = "Datacenter";
"ayu_MessageDetailsPackOwnerPC" = "Pack Owner";
"ayu_MessageDetailsPackOwnerFetchingPC" = "fetching...";
"ayu_MessageDetailsPackOwnerNotFoundPC" = "not found";
"ayu_KillApp" = "Kill App";
"ayu_LReadMessages" = "Read on Local";
"ayu_SReadMessages" = "Read on Server";

View file

@ -59,54 +59,64 @@ void initialize()
settings = AyuGramSettings();
sendReadMessagesReactive.value() | rpl::filter([=](bool val)
{
return (val != settings->sendReadMessages);
}) | start_with_next([=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings.value());
}, lifetime);
sendReadMessagesReactive.value() | rpl::filter(
[=](bool val)
{
return (val != settings->sendReadMessages);
}) | start_with_next(
[=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings.value());
}, lifetime);
// ..
sendReadStoriesReactive.value() | rpl::filter([=](bool val)
{
return (val != settings->sendReadStories);
}) | start_with_next([=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings.value());
}, lifetime);
sendReadStoriesReactive.value() | rpl::filter(
[=](bool val)
{
return (val != settings->sendReadStories);
}) | start_with_next(
[=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings.value());
}, lifetime);
// ..
sendOnlinePacketsReactive.value() | rpl::filter([=](bool val)
{
return (val != settings->sendOnlinePackets);
}) | start_with_next([=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings
.value());
}, lifetime);
sendOnlinePacketsReactive.value() | rpl::filter(
[=](bool val)
{
return (val != settings->sendOnlinePackets);
}) | start_with_next(
[=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings
.value());
}, lifetime);
// ..
sendUploadProgressReactive.value() | rpl::filter([=](bool val)
{
return (val != settings->sendUploadProgress);
}) | start_with_next([=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings
.value());
}, lifetime);
sendUploadProgressReactive.value() | rpl::filter(
[=](bool val)
{
return (val != settings->sendUploadProgress);
}) | start_with_next(
[=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(settings
.value());
}, lifetime);
// ..
sendOfflinePacketAfterOnlineReactive.value() | rpl::filter([=](bool val)
{
return (val
!= settings->sendOfflinePacketAfterOnline);
}) | start_with_next([=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(
settings.value());
}, lifetime);
sendOfflinePacketAfterOnlineReactive.value() | rpl::filter(
[=](bool val)
{
return (val
!= settings->sendOfflinePacketAfterOnline);
}) | start_with_next(
[=](bool val)
{
ghostModeEnabled =
ghostModeEnabled_util(
settings.value());
}, lifetime);
}
void postinitialize()
@ -150,7 +160,8 @@ void load()
catch (...) {
LOG(("AyuGramSettings: failed to parse settings file"));
}
} catch (...) {
}
catch (...) {
LOG(("AyuGramSettings: failed to read settings file (not json-like)"));
}
postinitialize();
@ -279,7 +290,8 @@ void AyuGramSettings::set_appIcon(QString val)
appIcon = std::move(val);
}
void AyuGramSettings::set_simpleQuotesAndReplies(bool val) {
void AyuGramSettings::set_simpleQuotesAndReplies(bool val)
{
simpleQuotesAndReplies = val;
}
@ -300,6 +312,31 @@ void AyuGramSettings::set_recentStickersCount(int val)
recentStickersCount = val;
}
void AyuGramSettings::set_showReactionsPanelInContextMenu(int val)
{
showReactionsPanelInContextMenu = val;
}
void AyuGramSettings::set_showViewsPanelInContextMenu(int val)
{
showViewsPanelInContextMenu = val;
}
void AyuGramSettings::set_showHideMessageInContextMenu(int val)
{
showHideMessageInContextMenu = val;
}
void AyuGramSettings::set_showUserMessagesInContextMenu(int val)
{
showUserMessagesInContextMenu = val;
}
void AyuGramSettings::set_showMessageDetailsInContextMenu(int val)
{
showMessageDetailsInContextMenu = val;
}
void AyuGramSettings::set_showLReadToggleInDrawer(bool val)
{
showLReadToggleInDrawer = val;

View file

@ -67,6 +67,15 @@ public:
editedMark = tr::lng_edited(tr::now);
recentStickersCount = 50;
// context menu items
// 0 - hide
// 1 - show normally
// 2 - show with SHIFT or CTRL pressed
showReactionsPanelInContextMenu = 1;
showViewsPanelInContextMenu = 1;
showUserMessagesInContextMenu = 2;
showMessageDetailsInContextMenu = 2;
showLReadToggleInDrawer = true;
showSReadToggleInDrawer = true;
showGhostToggleInDrawer = true;
@ -99,12 +108,15 @@ public:
bool sendOnlinePackets;
bool sendUploadProgress;
bool sendOfflinePacketAfterOnline;
bool markReadAfterSend;
bool markReadAfterReaction;
bool markReadAfterPoll;
bool useScheduledMessages;
bool saveDeletedMessages;
bool saveMessagesHistory;
bool disableAds;
bool disableStories;
bool collapseSimilarChannels;
@ -112,101 +124,92 @@ public:
bool disableNotificationsDelay;
bool localPremium;
bool copyUsernameAsLink;
QString appIcon;
bool simpleQuotesAndReplies;
QString deletedMark;
QString editedMark;
int recentStickersCount;
int showReactionsPanelInContextMenu;
int showViewsPanelInContextMenu;
int showHideMessageInContextMenu;
int showUserMessagesInContextMenu;
int showMessageDetailsInContextMenu;
bool showLReadToggleInDrawer;
bool showSReadToggleInDrawer;
bool showGhostToggleInDrawer;
bool showStreamerToggleInDrawer;
bool showGhostToggleInTray;
bool showStreamerToggleInTray;
QString mainFont;
QString monoFont;
int showPeerId;
bool hideAllChatsFolder;
bool showMessageSeconds;
bool stickerConfirmation;
bool gifConfirmation;
bool voiceConfirmation;
public:
void set_sendReadMessages(bool val);
void set_sendReadStories(bool val);
void set_sendOnlinePackets(bool val);
void set_sendUploadProgress(bool val);
void set_sendOfflinePacketAfterOnline(bool val);
void set_ghostModeEnabled(bool val);
void set_markReadAfterSend(bool val);
void set_markReadAfterReaction(bool val);
void set_markReadAfterPoll(bool val);
void set_useScheduledMessages(bool val);
void set_saveDeletedMessages(bool val);
void set_saveMessagesHistory(bool val);
void set_disableAds(bool val);
void set_disableStories(bool val);
void set_collapseSimilarChannels(bool val);
void set_hideSimilarChannels(bool val);
void set_disableNotificationsDelay(bool val);
void set_localPremium(bool val);
void set_copyUsernameAsLink(bool val);
void set_appIcon(QString val);
void set_simpleQuotesAndReplies(bool val);
void set_deletedMark(QString val);
void set_editedMark(QString val);
void set_recentStickersCount(int val);
void set_showReactionsPanelInContextMenu(int val);
void set_showViewsPanelInContextMenu(int val);
void set_showHideMessageInContextMenu(int val);
void set_showUserMessagesInContextMenu(int val);
void set_showMessageDetailsInContextMenu(int val);
void set_showLReadToggleInDrawer(bool val);
void set_showSReadToggleInDrawer(bool val);
void set_showGhostToggleInDrawer(bool val);
void set_showStreamerToggleInDrawer(bool val);
void set_showGhostToggleInTray(bool val);
void set_showStreamerToggleInTray(bool val);
void set_mainFont(QString val);
void set_monoFont(QString val);
void set_showPeerId(int val);
void set_showMessageSeconds(bool val);
void set_hideAllChatsFolder(bool val);
void set_stickerConfirmation(bool val);
void set_gifConfirmation(bool val);
void set_voiceConfirmation(bool val);
};
@ -235,6 +238,11 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
deletedMark,
editedMark,
recentStickersCount,
showReactionsPanelInContextMenu,
showViewsPanelInContextMenu,
showHideMessageInContextMenu,
showUserMessagesInContextMenu,
showMessageDetailsInContextMenu,
showLReadToggleInDrawer,
showSReadToggleInDrawer,
showGhostToggleInDrawer,
@ -254,17 +262,14 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
AyuGramSettings &getInstance();
void load();
void save();
rpl::producer<QString> get_deletedMarkReactive();
rpl::producer<QString> get_editedMarkReactive();
rpl::producer<int> get_showPeerIdReactive();
bool get_ghostModeEnabled();
rpl::producer<bool> get_ghostModeEnabledReactive();
}

View file

@ -8,4 +8,31 @@
namespace AyuState
{
std::unordered_map<PeerId, std::unordered_set<MsgId>> hiddenMessages;
void hide(PeerId peerId, MsgId messageId)
{
hiddenMessages[peerId].insert(messageId);
}
void hide(not_null<HistoryItem *> item)
{
hide(item->history()->peer->id, item->id);
}
bool isHidden(PeerId peerId, MsgId messageId)
{
auto it = hiddenMessages.find(peerId);
if (it != hiddenMessages.end()) {
return it->second.find(messageId) != it->second.end();
}
return false;
}
bool isHidden(not_null<HistoryItem *> item)
{
return isHidden(item->history()->peer->id, item->id);
}
}

View file

@ -8,37 +8,15 @@
#include "ayu_settings.h"
#include "history/history.h"
#include "history/history_item.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 hide(PeerId peerId, MsgId messageId);
void hide(not_null<HistoryItem *> item);
bool isHidden(PeerId peerId, MsgId messageId);
bool isHidden(not_null<HistoryItem *> item);
}

View file

@ -5,13 +5,18 @@
//
// Copyright @Radolyn, 2023
#include "ayu/ui/context_menu/context_menu.h"
#include "ayu/utils/qt_key_modifiers_extended.h"
#include "history/history_item_components.h"
#include "lang_auto.h"
#include "ayu/ayu_state.h"
#include "ayu/messages/ayu_messages_controller.h"
#include "ayu/ui/boxes/message_history_box.h"
#include "ayu/ui/context_menu/menu_item_subtext.h"
#include "mainwidget.h"
#include "core/mime_type.h"
#include "styles/style_ayu_icons.h"
#include "window/window_peer_menu.h"
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
#include "ui/widgets/popup_menu.h"
#include "ayu/ui/sections/edited/edited_log_section.h"
@ -21,6 +26,11 @@
namespace AyuUi
{
bool needToShowItem(int state)
{
return state == 1 || state == 2 && base::IsExtendedContextMenuModifierPressed();
}
void AddHistoryAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
{
if (AyuMessages::getInstance().hasRevisions(item)) {
@ -34,21 +44,28 @@ void AddHistoryAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
void AddHideMessageAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
{
// todo: rework
// const auto settings = &AyuSettings::getInstance();
// const auto history = item->history();
// menu->addAction(tr::ayu_ContextHideMessage(tr::now), [=]()
// {
// const auto initSaveDeleted = settings->saveDeletedMessages;
//
// settings->set_saveDeletedMessages(false);
// history->destroyMessage(item);
// settings->set_saveDeletedMessages(initSaveDeleted);
// }, &st::menuIconClear);
const auto settings = &AyuSettings::getInstance();
if (!needToShowItem(settings->showHideMessageInContextMenu)) {
return;
}
const auto history = item->history();
menu->addAction(tr::ayu_ContextHideMessage(tr::now), [=]()
{
item->destroy();
history->requestChatListMessage();
AyuState::hide(item);
}, &st::menuIconClear);
}
void AddUserMessagesAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
{
const auto settings = &AyuSettings::getInstance();
if (!needToShowItem(settings->showUserMessagesInContextMenu)) {
return;
}
if (item->history()->peer->isChat() || item->history()->peer->isMegagroup()) {
menu->addAction(tr::ayu_UserMessagesMenuText(tr::now), [=]
{
@ -60,15 +77,202 @@ void AddUserMessagesAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
? Dialogs::Key{item->topic()}
: Dialogs::Key{item->history()}
: Dialogs::Key();
mainWidget->content()->searchMessages("", key, item->from()->asUser());
mainWidget->content()->searchMessages(QString(), key, item->from()->asUser());
}
}
}, &st::menuIconInfo);
}, &st::menuIconTTL);
}
}
void AddMessageDetailsAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
{
const auto settings = &AyuSettings::getInstance();
if (!needToShowItem(settings->showMessageDetailsInContextMenu)) {
return;
}
if (item->isLocal()) {
return;
}
const auto view = item->mainView();
const auto forwarded = item->Get<HistoryMessageForwarded>();
const auto views = item->Get<HistoryMessageViews>();
const auto media = item->media();
const auto isSticker = media && media->document() && media->document()->sticker();
const auto isForwarded = forwarded && !forwarded->story && forwarded->psaType.isEmpty();
const auto messageId = QString::number(item->id.bare);
const auto messageDate = base::unixtime::parse(item->date());
const auto messageEditDate = base::unixtime::parse(view ? view->displayedEditDate() : TimeId(0));
#pragma clang diagnostic push
#pragma ide diagnostic ignored "NullDereference"
const auto messageForwardedDate =
isForwarded ? base::unixtime::parse(forwarded->originalDate)
: QDateTime();
#pragma clang diagnostic pop
const auto
messageViews = item->hasViews() && item->viewsCount() > 0 ? QString::number(item->viewsCount()) : QString();
const auto messageForwards = views && views->forwardsCount > 0 ? QString::number(views->forwardsCount) : QString();
const auto mediaSize = media ? getMediaSize(item) : QString();
const auto mediaMime = media ? getMediaMime(item) : QString();
// todo: bitrate (?)
const auto mediaName = media ? getMediaName(item) : QString();
const auto mediaResolution = media ? getMediaResolution(item) : QString();
const auto mediaDC = media ? getMediaDC(item) : QString();
const auto hasAnyPostField =
!messageViews.isEmpty() ||
!messageForwards.isEmpty();
const auto hasAnyMediaField =
!mediaSize.isEmpty() ||
!mediaMime.isEmpty() ||
!mediaName.isEmpty() ||
!mediaResolution.isEmpty() ||
!mediaDC.isEmpty();
const auto callback = Ui::Menu::CreateAddActionCallback(menu);
callback(Window::PeerMenuCallback::Args{
.text = tr::ayu_MessageDetailsPC(tr::now),
.handler = nullptr,
.icon = &st::menuIconInfo,
.fillSubmenu = [&](not_null<Ui::PopupMenu *> menu2)
{
if (hasAnyPostField) {
if (!messageViews.isEmpty()) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconShowInChat,
tr::ayu_MessageDetailsViewsPC(tr::now),
messageViews
));
}
if (!messageForwards.isEmpty()) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconViewReplies,
tr::ayu_MessageDetailsSharesPC(tr::now),
messageForwards
));
}
menu2->addSeparator();
}
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconInfo,
QString("ID"),
messageId
));
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconSchedule,
tr::ayu_MessageDetailsDatePC(tr::now),
formatDateTime(messageDate)
));
if (view && view->displayedEditDate()) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconEdit,
tr::ayu_MessageDetailsEditedDatePC(tr::now),
formatDateTime(messageEditDate)
));
}
if (isForwarded) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconTTL,
tr::ayu_MessageDetailsForwardedDatePC(tr::now),
formatDateTime(messageForwardedDate)
));
}
if (media && hasAnyMediaField) {
menu2->addSeparator();
if (!mediaSize.isEmpty()) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconDownload,
tr::ayu_MessageDetailsFileSizePC(tr::now),
mediaSize
));
}
if (!mediaMime.isEmpty()) {
const auto mime = Core::MimeTypeForName(mediaMime);
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconShowAll,
tr::ayu_MessageDetailsMimeTypePC(tr::now),
mime.name()
));
}
if (!mediaName.isEmpty()) {
auto const shortified = mediaName.length() > 20 ? "" + mediaName.right(20) : mediaName;
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::ayuEditsHistoryIcon,
tr::ayu_MessageDetailsFileNamePC(tr::now),
shortified,
[=]
{
QGuiApplication::clipboard()->setText(mediaName);
}
));
}
if (!mediaResolution.isEmpty()) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconStats,
tr::ayu_MessageDetailsResolutionPC(tr::now),
mediaResolution
));
}
if (!mediaDC.isEmpty()) {
menu2->addAction(Ui::ContextActionWithSubText(
menu2->menu(),
st::menuIconBoosts,
tr::ayu_MessageDetailsDatacenterPC(tr::now),
mediaDC
));
}
if (isSticker) {
const auto authorId = media->document()->sticker()->set.id >> 32;
menu2->addAction(Ui::ContextActionStickerAuthor(
menu2->menu(),
&item->history()->session(),
authorId
));
}
}
},
});
}
void AddReadUntilAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
{
if (item->isLocal()) {
return;
}
const auto settings = &AyuSettings::getInstance();
if (settings->sendReadMessages) {
return;

View file

@ -78,17 +78,23 @@
#include "history/view/history_view_context_menu.h"
#include "ayu/ayu_settings.h"
#include "ayu/utils/qt_key_modifiers_extended.h"
#include "styles/style_info.h"
namespace AyuUi
{
bool needToShowItem(int state);
void AddHistoryAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item);
void AddHideMessageAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item);
void AddUserMessagesAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item);
void AddMessageDetailsAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item);
void AddReadUntilAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item);
}

View file

@ -0,0 +1,301 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include <utility>
#include "ayu/ui/context_menu/menu_item_subtext.h"
#include "ayu/utils/telegram_helpers.h"
#include "ayu/database/entities.h"
#include "mainwindow.h"
#include "qguiapplication.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "base/unixtime.h"
#include "styles/style_chat.h"
#include "styles/style_menu_icons.h"
#include "window/window_session_controller.h"
namespace Ui
{
namespace
{
class ActionWithSubText : public Menu::ItemBase
{
public:
ActionWithSubText(
not_null<RpWidget *> parent,
const style::Menu &st,
const style::icon &icon,
Fn<void()> callback,
const QString &title,
QString subtext);
bool isEnabled() const override;
not_null<QAction *> action() const override;
void handleKeyPress(not_null<QKeyEvent *> e) override;
protected:
QPoint prepareRippleStartPosition() const override;
QImage prepareRippleMask() const override;
int contentHeight() const override;
void prepare(const QString &title);
void paint(Painter &p);
const not_null<QAction *> _dummyAction;
const style::Menu &_st;
const style::icon &_icon;
Text::String _title;
int _textWidth = 0;
QString _subText;
const int _height;
};
class ActionStickerPackAuthor final : public ActionWithSubText
{
public:
ActionStickerPackAuthor(not_null<Menu::Menu *> menu, not_null<Main::Session *> session, ID authorId);
private:
not_null<Main::Session *> _session;
void searchAuthor(ID authorId);
};
TextParseOptions MenuTextOptions = {
TextParseLinks, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
ActionWithSubText::ActionWithSubText(
not_null<RpWidget *> parent,
const style::Menu &st,
const style::icon &icon,
Fn<void()> callback,
const QString &title,
QString subtext)
: ItemBase(parent, st),
_dummyAction(new QAction(parent)),
_st(st),
_icon(icon),
_subText(std::move(subtext)),
_height(st::ttlItemPadding.top()
+ _st.itemStyle.font->height
+ st::ttlItemTimerFont->height
+ st::ttlItemPadding.bottom())
{
setAcceptBoth(true);
initResizeHook(parent->sizeValue());
setClickedCallback(std::move(callback));
paintRequest(
) | rpl::start_with_next([=]
{
Painter p(this);
paint(p);
}, lifetime());
enableMouseSelecting();
prepare(title);
}
void ActionWithSubText::paint(Painter &p)
{
const auto selected = isSelected();
if (selected && _st.itemBgOver->c.alpha() < 255) {
p.fillRect(0, 0, width(), _height, _st.itemBg);
}
p.fillRect(0, 0, width(), _height, selected ? _st.itemBgOver : _st.itemBg);
if (isEnabled()) {
paintRipple(p, 0, 0);
}
const auto normalHeight = _st.itemPadding.top()
+ _st.itemStyle.font->height
+ _st.itemPadding.bottom();
const auto deltaHeight = _height - normalHeight;
_icon.paint(
p,
_st.itemIconPosition + QPoint(0, deltaHeight / 2),
width());
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
_title.drawLeftElided(
p,
_st.itemPadding.left(),
st::ttlItemPadding.top(),
_textWidth,
width());
p.setFont(st::ttlItemTimerFont);
p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut);
p.drawTextLeft(
_st.itemPadding.left(),
st::ttlItemPadding.top() + _st.itemStyle.font->height,
width(),
_subText);
}
void ActionWithSubText::prepare(const QString &title)
{
_title.setMarkedText(
_st.itemStyle,
{title},
MenuTextOptions);
const auto textWidth = _title.maxWidth();
const auto &padding = _st.itemPadding;
const auto goodWidth = padding.left()
+ textWidth
+ padding.right();
const auto subTextMaxWidth = padding.left()
+ st::ttlItemTimerFont->width(_subText)
+ padding.right();
const auto w = std::clamp(
std::max({goodWidth, subTextMaxWidth}),
_st.widthMin,
_st.widthMax);
_textWidth = w - (goodWidth - textWidth);
setMinWidth(w);
update();
}
bool ActionWithSubText::isEnabled() const
{
return true;
}
not_null<QAction *> ActionWithSubText::action() const
{
return _dummyAction;
}
QPoint ActionWithSubText::prepareRippleStartPosition() const
{
return mapFromGlobal(QCursor::pos());
}
QImage ActionWithSubText::prepareRippleMask() const
{
return Ui::RippleAnimation::RectMask(size());
}
int ActionWithSubText::contentHeight() const
{
return _height;
}
void ActionWithSubText::handleKeyPress(not_null<QKeyEvent *> e)
{
if (!isSelected()) {
return;
}
const auto key = e->key();
if (key == Qt::Key_Enter || key == Qt::Key_Return) {
setClicked(Menu::TriggeredSource::Keyboard);
}
}
ActionStickerPackAuthor::ActionStickerPackAuthor(not_null<Menu::Menu *> menu,
not_null<Main::Session *> session,
ID authorId)
: ActionWithSubText(menu, menu->st(), st::menuIconStickers, [=]
{ }, tr::ayu_MessageDetailsPackOwnerPC(tr::now), tr::ayu_MessageDetailsPackOwnerFetchingPC(tr::now)),
_session(session)
{
searchAuthor(authorId);
}
void ActionStickerPackAuthor::searchAuthor(ID authorId)
{
searchById(authorId, _session, [=](const QString &username, UserData *user)
{
if (username.isEmpty() && !user) {
_subText = tr::ayu_MessageDetailsPackOwnerNotFoundPC(tr::now);
setClickedCallback([=] {
const auto text = QString("int32: %1\nint64: %2").arg(authorId).arg(0x100000000L + authorId);
QGuiApplication::clipboard()->setText(text);
});
crl::on_main([=]
{
update();
});
return;
}
const auto title = username.isEmpty() ? user ? user->name() : QString() : username;
const auto callback = [=]
{
if (user) {
if (const auto window = _session->tryResolveWindow()) {
if (const auto mainWidget = window->widget()->sessionController()) {
mainWidget->showPeer(user);
}
}
}
else {
QGuiApplication::clipboard()->setText(title);
}
};
setClickedCallback(callback);
_subText = title;
crl::on_main([=]
{
update();
});
});
}
} // namespace
base::unique_qptr<Menu::ItemBase> ContextActionWithSubText(
not_null<Menu::Menu *> menu,
const style::icon &icon,
const QString &title,
const QString &subtext,
Fn<void()> callback)
{
if (!callback) {
callback = [=]()
{
QGuiApplication::clipboard()->setText(subtext);
};
}
return base::make_unique_q<ActionWithSubText>(
menu,
menu->st(),
icon,
std::move(callback),
title,
subtext);
}
base::unique_qptr<Menu::ItemBase> ContextActionStickerAuthor(
not_null<Menu::Menu *> menu,
not_null<Main::Session *> session,
ID authorId)
{
return base::make_unique_q<ActionStickerPackAuthor>(menu, session, authorId);
}
} // namespace Ui

View file

@ -0,0 +1,36 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "ayu/database/entities.h"
#include "main/main_session.h"
#include "base/unique_qptr.h"
namespace Ui
{
namespace Menu
{
class Menu;
class ItemBase;
} // namespace Menu
class PopupMenu;
[[nodiscard]] base::unique_qptr<Menu::ItemBase> ContextActionWithSubText(
not_null<Menu::Menu *> menu,
const style::icon &icon,
const QString &title,
const QString &subtext,
Fn<void()> callback = nullptr);
[[nodiscard]] base::unique_qptr<Menu::ItemBase> ContextActionStickerAuthor(
not_null<Menu::Menu *> menu,
not_null<Main::Session *> session,
ID authorId);
} // namespace Ui

View file

@ -28,6 +28,7 @@
#include "styles/style_basic.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"
#include "styles/style_widgets.h"
#include "styles/style_ayu_styles.h"
@ -333,6 +334,50 @@ void AddCollapsibleToggle(not_null<Ui::VerticalLayout *> container,
}, raw->lifetime());
}
void AddChooseButtonWithIconAndRightText(not_null<Ui::VerticalLayout *> container,
not_null<Window::SessionController *> controller,
int initialState,
std::vector<QString> options,
rpl::producer<QString> text,
rpl::producer<QString> boxTitle,
const style::icon &icon,
const Fn<void(int)> &setter)
{
auto reactiveVal = container->lifetime().make_state<rpl::variable<int>>(initialState);
rpl::producer<QString> rightTextReactive = reactiveVal->value() | rpl::map(
[=](int val)
{
return options[val];
});
Settings::AddButtonWithLabel(
container,
std::move(text),
rightTextReactive,
st::settingsButton,
{&icon})->addClickHandler(
[=]
{
controller->show(Box(
[=](not_null<Ui::GenericBox *> box)
{
const auto save = [=](int index) mutable
{
setter(index);
reactiveVal->force_assign(index);
};
SingleChoiceBox(box, {
.title = boxTitle,
.options = options,
.initialSelection = reactiveVal->current(),
.callback = save,
});
}));
});
}
namespace Settings
{
@ -436,22 +481,25 @@ void Ayu::SetupGhostEssentials(not_null<Ui::VerticalLayout *> container)
AddButtonWithIcon(
container,
tr::ayu_UseScheduledMessages() | rpl::map([=](QString val)
{
return val + " β";
}),
tr::ayu_UseScheduledMessages() | rpl::map(
[=](QString val)
{
return val + " β";
}),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->useScheduledMessages)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->useScheduledMessages);
}) | start_with_next([=](bool enabled)
{
settings->set_useScheduledMessages(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->useScheduledMessages);
}) | start_with_next(
[=](bool enabled)
{
settings->set_useScheduledMessages(enabled);
AyuSettings::save();
}, container->lifetime());
}
void Ayu::SetupSpyEssentials(not_null<Ui::VerticalLayout *> container)
@ -462,22 +510,25 @@ void Ayu::SetupSpyEssentials(not_null<Ui::VerticalLayout *> container)
AddButtonWithIcon(
container,
tr::ayu_SaveDeletedMessages() | rpl::map([=](QString val)
{
return val + " β";
}),
tr::ayu_SaveDeletedMessages() | rpl::map(
[=](QString val)
{
return val + " β";
}),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->saveDeletedMessages)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->saveDeletedMessages);
}) | start_with_next([=](bool enabled)
{
settings->set_saveDeletedMessages(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->saveDeletedMessages);
}) | start_with_next(
[=](bool enabled)
{
settings->set_saveDeletedMessages(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -486,14 +537,16 @@ void Ayu::SetupSpyEssentials(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->saveMessagesHistory)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->saveMessagesHistory);
}) | start_with_next([=](bool enabled)
{
settings->set_saveMessagesHistory(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->saveMessagesHistory);
}) | start_with_next(
[=](bool enabled)
{
settings->set_saveMessagesHistory(enabled);
AyuSettings::save();
}, container->lifetime());
}
void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
@ -509,14 +562,16 @@ void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->disableAds)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->disableAds);
}) | start_with_next([=](bool enabled)
{
settings->set_disableAds(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->disableAds);
}) | start_with_next(
[=](bool enabled)
{
settings->set_disableAds(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -525,14 +580,16 @@ void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->disableStories)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->disableStories);
}) | start_with_next([=](bool enabled)
{
settings->set_disableStories(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->disableStories);
}) | start_with_next(
[=](bool enabled)
{
settings->set_disableStories(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -541,14 +598,16 @@ void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->simpleQuotesAndReplies)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->simpleQuotesAndReplies);
}) | start_with_next([=](bool enabled)
{
settings->set_simpleQuotesAndReplies(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->simpleQuotesAndReplies);
}) | start_with_next(
[=](bool enabled)
{
settings->set_simpleQuotesAndReplies(enabled);
AyuSettings::save();
}, container->lifetime());
std::vector checkboxes = {
NestedEntry{
@ -580,14 +639,16 @@ void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->disableNotificationsDelay)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->disableNotificationsDelay);
}) | start_with_next([=](bool enabled)
{
settings->set_disableNotificationsDelay(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->disableNotificationsDelay);
}) | start_with_next(
[=](bool enabled)
{
settings->set_disableNotificationsDelay(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -596,14 +657,16 @@ void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->localPremium)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->localPremium);
}) | start_with_next([=](bool enabled)
{
settings->set_localPremium(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->localPremium);
}) | start_with_next(
[=](bool enabled)
{
settings->set_localPremium(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -612,14 +675,16 @@ void Ayu::SetupQoLToggles(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->copyUsernameAsLink)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->copyUsernameAsLink);
}) | start_with_next([=](bool enabled)
{
settings->set_copyUsernameAsLink(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->copyUsernameAsLink);
}) | start_with_next(
[=](bool enabled)
{
settings->set_copyUsernameAsLink(enabled);
AyuSettings::save();
}, container->lifetime());
}
void Ayu::SetupAppIcon(not_null<Ui::VerticalLayout *> container)
@ -647,11 +712,12 @@ void Ayu::SetupCustomization(not_null<Ui::VerticalLayout *> container,
AyuSettings::get_deletedMarkReactive(),
st::settingsButtonNoIcon
);
btn->addClickHandler([=]()
{
auto box = Box<EditDeletedMarkBox>();
Ui::show(std::move(box));
});
btn->addClickHandler(
[=]()
{
auto box = Box<EditDeletedMarkBox>();
Ui::show(std::move(box));
});
auto btn2 = AddButtonWithLabel(
container,
@ -659,11 +725,12 @@ void Ayu::SetupCustomization(not_null<Ui::VerticalLayout *> container,
AyuSettings::get_editedMarkReactive(),
st::settingsButtonNoIcon
);
btn2->addClickHandler([=]()
{
auto box = Box<EditEditedMarkBox>();
Ui::show(std::move(box));
});
btn2->addClickHandler(
[=]()
{
auto box = Box<EditEditedMarkBox>();
Ui::show(std::move(box));
});
AddSkip(container);
AddDivider(container);
@ -684,14 +751,16 @@ void Ayu::SetupCustomization(not_null<Ui::VerticalLayout *> container,
)->toggleOn(
rpl::single(settings->hideAllChatsFolder)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->hideAllChatsFolder);
}) | start_with_next([=](bool enabled)
{
settings->set_hideAllChatsFolder(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->hideAllChatsFolder);
}) | start_with_next(
[=](bool enabled)
{
settings->set_hideAllChatsFolder(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -700,17 +769,20 @@ void Ayu::SetupCustomization(not_null<Ui::VerticalLayout *> container,
)->toggleOn(
rpl::single(settings->showMessageSeconds)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showMessageSeconds);
}) | start_with_next([=](bool enabled)
{
settings->set_showMessageSeconds(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showMessageSeconds);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showMessageSeconds(enabled);
AyuSettings::save();
}, container->lifetime());
AddSkip(container);
AddDivider(container);
SetupContextMenuElements(container, controller);
SetupDrawerElements(container);
AddSkip(container);
AddDivider(container);
@ -721,6 +793,91 @@ void Ayu::SetupCustomization(not_null<Ui::VerticalLayout *> container,
SetupFonts(container, controller);
}
void Ayu::SetupContextMenuElements(not_null<Ui::VerticalLayout *> container,
not_null<Window::SessionController *> controller)
{
auto settings = &AyuSettings::getInstance();
AddSkip(container);
AddSubsectionTitle(container, tr::ayu_ContextMenuElementsHeader());
const auto options = std::vector{
tr::ayu_SettingsContextMenuItemHidden(tr::now),
tr::ayu_SettingsContextMenuItemShown(tr::now),
tr::ayu_SettingsContextMenuItemExtended(tr::now),
};
AddChooseButtonWithIconAndRightText(
container,
controller,
settings->showReactionsPanelInContextMenu,
options,
tr::ayu_SettingsContextMenuReactionsPanel(),
tr::ayu_SettingsContextMenuTitle(),
st::menuIconReactions,
[=](int index)
{
settings->set_showReactionsPanelInContextMenu(index);
AyuSettings::save();
});
AddChooseButtonWithIconAndRightText(
container,
controller,
settings->showViewsPanelInContextMenu,
options,
tr::ayu_SettingsContextMenuViewsPanel(),
tr::ayu_SettingsContextMenuTitle(),
st::menuIconShowInChat,
[=](int index)
{
settings->set_showViewsPanelInContextMenu(index);
AyuSettings::save();
});
AddChooseButtonWithIconAndRightText(
container,
controller,
settings->showHideMessageInContextMenu,
options,
tr::ayu_ContextHideMessage(),
tr::ayu_SettingsContextMenuTitle(),
st::menuIconClear,
[=](int index)
{
settings->set_showHideMessageInContextMenu(index);
AyuSettings::save();
});
AddChooseButtonWithIconAndRightText(
container,
controller,
settings->showUserMessagesInContextMenu,
options,
tr::ayu_UserMessagesMenuText(),
tr::ayu_SettingsContextMenuTitle(),
st::menuIconTTL,
[=](int index)
{
settings->set_showUserMessagesInContextMenu(index);
AyuSettings::save();
});
AddChooseButtonWithIconAndRightText(
container,
controller,
settings->showMessageDetailsInContextMenu,
options,
tr::ayu_MessageDetailsPC(),
tr::ayu_SettingsContextMenuTitle(),
st::menuIconInfo,
[=](int index)
{
settings->set_showMessageDetailsInContextMenu(index);
AyuSettings::save();
});
AddSkip(container);
AddDividerText(container, tr::ayu_SettingsContextMenuDescription());
}
void Ayu::SetupDrawerElements(not_null<Ui::VerticalLayout *> container)
{
auto settings = &AyuSettings::getInstance();
@ -736,14 +893,16 @@ void Ayu::SetupDrawerElements(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->showLReadToggleInDrawer)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showLReadToggleInDrawer);
}) | start_with_next([=](bool enabled)
{
settings->set_showLReadToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showLReadToggleInDrawer);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showLReadToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -753,14 +912,16 @@ void Ayu::SetupDrawerElements(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->showSReadToggleInDrawer)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showSReadToggleInDrawer);
}) | start_with_next([=](bool enabled)
{
settings->set_showSReadToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showSReadToggleInDrawer);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showSReadToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -770,14 +931,16 @@ void Ayu::SetupDrawerElements(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->showGhostToggleInDrawer)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showGhostToggleInDrawer);
}) | start_with_next([=](bool enabled)
{
settings->set_showGhostToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showGhostToggleInDrawer);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showGhostToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -787,14 +950,16 @@ void Ayu::SetupDrawerElements(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->showStreamerToggleInDrawer)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showStreamerToggleInDrawer);
}) | start_with_next([=](bool enabled)
{
settings->set_showStreamerToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showStreamerToggleInDrawer);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showStreamerToggleInDrawer(enabled);
AyuSettings::save();
}, container->lifetime());
}
void Ayu::SetupTrayElements(not_null<Ui::VerticalLayout *> container)
@ -811,14 +976,16 @@ void Ayu::SetupTrayElements(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->showGhostToggleInTray)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showGhostToggleInTray);
}) | start_with_next([=](bool enabled)
{
settings->set_showGhostToggleInTray(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showGhostToggleInTray);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showGhostToggleInTray(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -827,14 +994,16 @@ void Ayu::SetupTrayElements(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->showStreamerToggleInTray)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->showStreamerToggleInTray);
}) | start_with_next([=](bool enabled)
{
settings->set_showStreamerToggleInTray(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->showStreamerToggleInTray);
}) | start_with_next(
[=](bool enabled)
{
settings->set_showStreamerToggleInTray(enabled);
AyuSettings::save();
}, container->lifetime());
}
void Ayu::SetupShowPeerId(not_null<Ui::VerticalLayout *> container,
@ -848,33 +1017,36 @@ void Ayu::SetupShowPeerId(not_null<Ui::VerticalLayout *> container,
QString("Bot API")
};
auto currentVal = AyuSettings::get_showPeerIdReactive() | rpl::map([=](int val)
{
return options[val];
});
auto currentVal = AyuSettings::get_showPeerIdReactive() | rpl::map(
[=](int val)
{
return options[val];
});
const auto button = AddButtonWithLabel(
container,
tr::ayu_SettingsShowID(),
currentVal,
st::settingsButtonNoIcon);
button->addClickHandler([=]
{
controller->show(Box([=](not_null<Ui::GenericBox *> box)
{
const auto save = [=](int index)
{
settings->set_showPeerId(index);
AyuSettings::save();
};
SingleChoiceBox(box, {
.title = tr::ayu_SettingsShowID(),
.options = options,
.initialSelection = settings->showPeerId,
.callback = save,
});
}));
});
button->addClickHandler(
[=]
{
controller->show(Box(
[=](not_null<Ui::GenericBox *> box)
{
const auto save = [=](int index)
{
settings->set_showPeerId(index);
AyuSettings::save();
};
SingleChoiceBox(box, {
.title = tr::ayu_SettingsShowID(),
.options = options,
.initialSelection = settings->showPeerId,
.callback = save,
});
}));
});
}
void Ayu::SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout *> container)
@ -932,16 +1104,17 @@ void Ayu::SetupFonts(not_null<Ui::VerticalLayout *> container, not_null<Window::
st::settingsButtonNoIcon);
const auto commonGuard = Ui::CreateChild<base::binary_guard>(commonButton.get());
commonButton->addClickHandler([=]
{
*commonGuard = AyuUi::FontSelectorBox::Show(controller, [=](QString font)
{
auto ayuSettings = &AyuSettings::getInstance();
commonButton->addClickHandler(
[=]
{
*commonGuard = AyuUi::FontSelectorBox::Show(controller, [=](QString font)
{
auto ayuSettings = &AyuSettings::getInstance();
ayuSettings->set_mainFont(std::move(font));
AyuSettings::save();
});
});
ayuSettings->set_mainFont(std::move(font));
AyuSettings::save();
});
});
const auto monoButton = AddButtonWithLabel(
container,
@ -952,16 +1125,17 @@ void Ayu::SetupFonts(not_null<Ui::VerticalLayout *> container, not_null<Window::
st::settingsButtonNoIcon);
const auto monoGuard = Ui::CreateChild<base::binary_guard>(monoButton.get());
monoButton->addClickHandler([=]
{
*monoGuard = AyuUi::FontSelectorBox::Show(controller, [=](QString font)
{
auto ayuSettings = &AyuSettings::getInstance();
monoButton->addClickHandler(
[=]
{
*monoGuard = AyuUi::FontSelectorBox::Show(controller, [=](QString font)
{
auto ayuSettings = &AyuSettings::getInstance();
ayuSettings->set_monoFont(std::move(font));
AyuSettings::save();
});
});
ayuSettings->set_monoFont(std::move(font));
AyuSettings::save();
});
});
}
@ -975,11 +1149,12 @@ void Ayu::SetupAyuSync(not_null<Ui::VerticalLayout *> container)
container,
text,
st::settingsButtonNoIcon
)->addClickHandler([=]
{
auto controller = &AyuSync::getInstance();
controller->initializeAgent();
});
)->addClickHandler(
[=]
{
auto controller = &AyuSync::getInstance();
controller->initializeAgent();
});
}
void Ayu::SetupSendConfirmations(not_null<Ui::VerticalLayout *> container)
@ -995,14 +1170,16 @@ void Ayu::SetupSendConfirmations(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->stickerConfirmation)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->stickerConfirmation);
}) | start_with_next([=](bool enabled)
{
settings->set_stickerConfirmation(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->stickerConfirmation);
}) | start_with_next(
[=](bool enabled)
{
settings->set_stickerConfirmation(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -1011,14 +1188,16 @@ void Ayu::SetupSendConfirmations(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->gifConfirmation)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->gifConfirmation);
}) | start_with_next([=](bool enabled)
{
settings->set_gifConfirmation(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->gifConfirmation);
}) | start_with_next(
[=](bool enabled)
{
settings->set_gifConfirmation(enabled);
AyuSettings::save();
}, container->lifetime());
AddButtonWithIcon(
container,
@ -1027,14 +1206,16 @@ void Ayu::SetupSendConfirmations(not_null<Ui::VerticalLayout *> container)
)->toggleOn(
rpl::single(settings->voiceConfirmation)
)->toggledValue(
) | rpl::filter([=](bool enabled)
{
return (enabled != settings->voiceConfirmation);
}) | start_with_next([=](bool enabled)
{
settings->set_voiceConfirmation(enabled);
AyuSettings::save();
}, container->lifetime());
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->voiceConfirmation);
}) | start_with_next(
[=](bool enabled)
{
settings->set_voiceConfirmation(enabled);
AyuSettings::save();
}, container->lifetime());
}
void Ayu::SetupAyuGramSettings(not_null<Ui::VerticalLayout *> container,

View file

@ -44,6 +44,9 @@ private:
void SetupCustomization(not_null<Ui::VerticalLayout *> container,
not_null<Window::SessionController *> controller);
void SetupContextMenuElements(not_null<Ui::VerticalLayout *> container,
not_null<Window::SessionController *> controller);
void SetupDrawerElements(not_null<Ui::VerticalLayout *> container);
void SetupTrayElements(not_null<Ui::VerticalLayout *> container);

View file

@ -0,0 +1,21 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "base/qt/qt_key_modifiers.h"
namespace base {
[[nodiscard]] inline bool IsShiftPressed() {
return (QGuiApplication::keyboardModifiers() == Qt::ShiftModifier);
}
[[nodiscard]] inline bool IsExtendedContextMenuModifierPressed() {
return IsShiftPressed() || IsCtrlPressed();
}
} // namespace base

View file

@ -10,10 +10,16 @@
#include "api/api_text_entities.h"
#include "core/mime_type.h"
#include "data/data_channel.h"
#include "data/data_document_media.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "inline_bots/inline_bot_result.h"
#include "lang_auto.h"
#include "apiwrap.h"
#include "data/data_forum.h"
#include "data/data_user.h"
#include "data/data_forum_topic.h"
#include "data/data_histories.h"
#include "data/data_peer_id.h"
@ -21,10 +27,12 @@
#include "ayu/sync/models.h"
#include "data/data_session.h"
#include "data/data_document.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_unread_things.h"
#include "data/data_histories.h"
#include "ui/text/format_values.h"
// https://github.com/AyuGram/AyuGram4AX/blob/rewrite/TMessagesProj/src/main/java/com/radolyn/ayugram/AyuConstants.java
std::unordered_set<ID> ayugram_channels = {
@ -328,4 +336,382 @@ QString formatTTL(int time)
}
return QString("🕓 %1s").arg(time);
}
}
QString getDCName(int dc)
{
const auto getName = [=](int dc)
{
switch (dc) {
case 1:
case 3: return "Miami FL, USA";
case 2:
case 4: return "Amsterdam, NL";
case 5: return "Singapore, SG";
default: return "UNKNOWN";
}
};
if (dc < 1) {
return {"DC_UNKNOWN"};
}
return QString("DC%1, %2").arg(dc).arg(getName(dc));
}
QString getLocalizedAt()
{
static const auto val = tr::lng_mediaview_date_time(
tr::now,
lt_date,
"",
lt_time,
"");
return val;
}
QString formatDateTime(const QDateTime &date)
{
const auto locale = QLocale::system();
const auto datePart = locale.toString(date.date(), QLocale::ShortFormat);
const auto timePart = locale.toString(date, "HH:mm:ss");
return datePart + getLocalizedAt() + timePart;
}
QString getMediaSize(not_null<HistoryItem *> message)
{
if (!message->media()) {
return {};
}
const auto media = message->media();
const auto document = media->document();
const auto photo = media->photo();
int64 size = -1;
if (document) { // any file
size = document->size;
}
else if (photo && photo->hasVideo()) { // video
size = photo->videoByteSize(Data::PhotoSize::Large);
if (size == 0) {
size = photo->videoByteSize(Data::PhotoSize::Small);
}
if (size == 0) {
size = photo->videoByteSize(Data::PhotoSize::Thumbnail);
}
}
else if (photo && !photo->hasVideo()) { // photo
size = photo->imageByteSize(Data::PhotoSize::Large);
if (size == 0) {
size = photo->imageByteSize(Data::PhotoSize::Small);
}
if (size == 0) {
size = photo->imageByteSize(Data::PhotoSize::Thumbnail);
}
}
if (size == -1) {
return {};
}
return Ui::FormatSizeText(size);
}
QString getMediaMime(not_null<HistoryItem *> message)
{
if (!message->media()) {
return {};
}
const auto media = message->media();
const auto document = media->document();
const auto photo = media->photo();
if (document) { // any file
return document->mimeString();
}
else if (photo && photo->hasVideo()) { // video
return "video/mp4";
}
else if (photo && !photo->hasVideo()) { // photo
return "image/jpeg";
}
return {};
}
QString getMediaName(not_null<HistoryItem *> message)
{
if (!message->media()) {
return {};
}
const auto media = message->media();
const auto document = media->document();
if (document) {
return document->filename();
}
return {};
}
QString getMediaResolution(not_null<HistoryItem *> message)
{
if (!message->media()) {
return {};
}
const auto media = message->media();
const auto document = media->document();
const auto photo = media->photo();
const auto formatQSize = [=](QSize size)
{
if (size.isNull() || size.isEmpty() || !size.isValid()) {
return QString();
}
return QString("%1x%2").arg(size.width()).arg(size.height());
};
if (document) {
return formatQSize(document->dimensions);
}
else if (photo) {
auto result = photo->size(Data::PhotoSize::Large);
if (!result.has_value()) {
result = photo->size(Data::PhotoSize::Small);
}
if (!result.has_value()) {
result = photo->size(Data::PhotoSize::Thumbnail);
}
return result.has_value() ? formatQSize(result.value()) : QString();
}
return {};
}
QString getMediaDC(not_null<HistoryItem *> message)
{
if (!message->media()) {
return {};
}
const auto media = message->media();
const auto document = media->document();
const auto photo = media->photo();
if (document) {
return getDCName(document->getDC());
}
else if (photo) {
return getDCName(photo->getDC());
}
return {};
}
void resolveUser(ID userId, const QString &username, Main::Session *session, const Callback &callback)
{
auto normalized = username.trimmed().toLower();
if (normalized.isEmpty()) {
callback(QString(), nullptr);
return;
}
normalized = normalized.startsWith("@") ? normalized.mid(1) : normalized;
if (normalized.isEmpty()) {
callback(QString(), nullptr);
return;
}
session->api().request(MTPcontacts_ResolveUsername(
MTP_string(normalized)
)).done([=](const MTPcontacts_ResolvedPeer &result)
{
Expects(result.type() == mtpc_contacts_resolvedPeer);
auto &data = result.c_contacts_resolvedPeer();
session->data().processUsers(data.vusers());
session->data().processChats(data.vchats());
const auto peer = session->data().peerLoaded(
peerFromMTP(data.vpeer()));
if (const auto user = peer ? peer->asUser() : nullptr) {
if (user->id.value == userId) {
callback(normalized, user);
return;
}
}
callback(normalized, nullptr);
}).fail([=]
{
callback(QString(), nullptr);
}).send();
}
void searchUser(ID userId, Main::Session *session, bool searchUserFlag, bool cache, const Callback &callback)
{
const auto botId = 1696868284;
const auto bot = session->data().userLoaded(botId);
if (!bot) {
if (searchUserFlag) {
resolveUser(botId, "tgdb_bot", session, [=](const QString &title, UserData *data)
{
searchUser(userId, session, false, false, callback);
});
}
else {
callback(QString(), nullptr);
}
return;
}
session->api().request(MTPmessages_GetInlineBotResults(
MTP_flags(0),
bot->inputUser,
MTP_inputPeerEmpty(),
MTPInputGeoPoint(),
MTP_string(QString::number(userId)),
MTP_string("")
)).done([=](const MTPmessages_BotResults &result)
{
if (result.type() != mtpc_messages_botResults) {
callback(QString(), nullptr);
return;
}
auto &d = result.c_messages_botResults();
session->data().processUsers(d.vusers());
auto &v = d.vresults().v;
auto queryId = d.vquery_id().v;
auto added = 0;
for (const auto &res : v) {
const auto message = res.match(
[&](const MTPDbotInlineResult &data)
{
return &data.vsend_message();
}, [&](const MTPDbotInlineMediaResult &data)
{
return &data.vsend_message();
});
const auto text = message->match(
[&](const MTPDbotInlineMessageMediaAuto &data)
{
return QString();
}, [&](const MTPDbotInlineMessageText &data)
{
return qs(data.vmessage());
}, [&](const MTPDbotInlineMessageMediaGeo &data)
{
return QString();
}, [&](const MTPDbotInlineMessageMediaVenue &data)
{
return QString();
}, [&](const MTPDbotInlineMessageMediaContact &data)
{
return QString();
}, [&](const MTPDbotInlineMessageMediaInvoice &data)
{
return QString();
}, [&](const MTPDbotInlineMessageMediaWebPage &data)
{
return QString();
});
if (text.isEmpty()) {
continue;
}
ID id = 0; // 🆔
QString title; // 🏷
QString username; // 📧
for (const auto &line : text.split('\n')) {
if (line.startsWith("🆔")) {
id = line.mid(line.indexOf(':') + 1).toLongLong();
}
else if (line.startsWith("🏷")) {
title = line.mid(line.indexOf(':') + 1);
}
else if (line.startsWith("📧")) {
username = line.mid(line.indexOf(':') + 1);
}
}
if (id == 0) {
continue;
}
if (id != userId) {
continue;
}
if (!username.isEmpty()) {
resolveUser(id, username, session, [=](const QString &titleInner, UserData *data)
{
if (data) {
callback(titleInner, data);
}
else {
callback(title, nullptr);
}
});
return;
}
if (!title.isEmpty()) {
callback(title, nullptr);
}
}
callback(QString(), nullptr);
}).fail([=]
{
callback(QString(), nullptr);
}).handleAllErrors().send();
}
void searchById(ID userId, Main::Session *session, bool retry, const Callback &callback)
{
if (userId == 0) {
return;
}
const auto dataLoaded = session->data().userLoaded(userId);
if (dataLoaded) {
callback(dataLoaded->username(), dataLoaded);
return;
}
searchUser(userId, session, true, true, [=](const QString &title, UserData *data)
{
if (data && data->accessHash()) {
callback(title, data);
}
else {
if (retry) {
searchById(0x100000000 + userId, session, false, callback);
}
else {
callback(QString(), nullptr);
}
}
});
}
void searchById(ID userId, Main::Session *session, const Callback &callback)
{
searchById(userId, session, true, callback);
}

View file

@ -14,6 +14,8 @@
#include "main/main_session.h"
#include "dialogs/dialogs_main_list.h"
using Callback = Fn<void(const QString&, UserData *)>;
Main::Session *getSession(ID userId);
bool accountExists(ID userId);
void dispatchToMainThread(std::function<void()> callback, int delay = 0);
@ -32,3 +34,14 @@ void MarkAsReadThread(not_null<Data::Thread *> thread);
void readHistory(not_null<HistoryItem *> message);
QString formatTTL(int time);
QString formatDateTime(const QDateTime &date);
QString getDCName(int dc);
QString getMediaSize(not_null<HistoryItem *> message);
QString getMediaMime(not_null<HistoryItem *> message);
QString getMediaName(not_null<HistoryItem *> message);
QString getMediaResolution(not_null<HistoryItem *> message);
QString getMediaDC(not_null<HistoryItem *> message);
void searchById(ID userId, Main::Session *session, const Callback& callback);

View file

@ -78,6 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ayu/ayu_settings.h"
#include "ayu/database/ayu_database.h"
#include "ayu/messages/ayu_messages_controller.h"
#include "ayu/ayu_state.h"
namespace Data {
@ -2353,6 +2354,11 @@ void Session::registerMessage(not_null<HistoryItem*> item) {
const auto peerId = item->history()->peer->id;
const auto list = messagesListForInsert(peerId);
const auto itemId = item->id;
if (AyuState::isHidden(item)) {
return;
}
const auto i = list->find(itemId);
if (i != list->end()) {
LOG(("App Error: Trying to re-registerMessage()."));

View file

@ -65,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/ayu_state.h"
namespace {
@ -1427,6 +1428,10 @@ void History::viewReplaced(not_null<const Element*> was, Element *now) {
void History::addItemToBlock(not_null<HistoryItem*> item) {
Expects(!item->mainView());
if (AyuState::isHidden(item)) {
return;
}
auto block = prepareBlockForAddingItem();
block->messages.push_back(item->createView(_delegateMixin->delegate()));

View file

@ -2263,6 +2263,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
AyuUi::AddHistoryAction(_menu, item);
AyuUi::AddHideMessageAction(_menu, item);
AyuUi::AddUserMessagesAction(_menu, item);
AyuUi::AddMessageDetailsAction(_menu, item);
};
const auto addPhotoActions = [&](not_null<PhotoData*> photo, HistoryItem *item) {
const auto media = photo->activeMediaView();

View file

@ -961,6 +961,7 @@ void AddMessageActions(
AyuUi::AddHistoryAction(menu, request.item);
AyuUi::AddHideMessageAction(menu, request.item);
AyuUi::AddUserMessagesAction(menu, request.item);
AyuUi::AddMessageDetailsAction(menu, request.item);
}
AddPostLinkAction(menu, request);
@ -972,8 +973,7 @@ void AddMessageActions(
AddSelectionAction(menu, request, list);
AddRescheduleAction(menu, request, list);
if (request.item)
{
if (request.item) {
AyuUi::AddReadUntilAction(menu, request.item);
}
}
@ -1278,6 +1278,11 @@ void AddWhoReactedAction(
not_null<QWidget*> context,
not_null<HistoryItem*> item,
not_null<Window::SessionController*> controller) {
const auto settings = &AyuSettings::getInstance();
if (!AyuUi::needToShowItem(settings->showViewsPanelInContextMenu)) {
return;
}
const auto whoReadIds = std::make_shared<Api::WhoReadList>();
const auto weak = Ui::MakeWeak(menu.get());
const auto participantChosen = [=](uint64 id) {

View file

@ -272,9 +272,11 @@ QString DateTooltipText(not_null<Element*> view) {
msgsigned->postAuthor);
}
}
dateText += '\n';
dateText += "ID: ";
dateText += QString::number(item->id.bare);
if (!item->isLocal()) { // local messages have strange ID
dateText += '\n';
dateText += "ID: ";
dateText += QString::number(item->id.bare);
}
return dateText;
}

View file

@ -29,6 +29,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_chat.h"
// AyuGram includes
#include "ayu/ui/context_menu/context_menu.h"
#include "ayu/ayu_settings.h"
namespace HistoryView::Reactions {
namespace {
@ -932,6 +937,11 @@ bool AdjustMenuGeometryForSelector(
not_null<Ui::PopupMenu*> menu,
QPoint desiredPosition,
not_null<Selector*> selector) {
const auto settings = &AyuSettings::getInstance();
if (!AyuUi::needToShowItem(settings->showReactionsPanelInContextMenu)) {
return false;
}
const auto useTransparency = selector->useTransparency();
const auto extend = useTransparency
? st::reactStripExtend
@ -1072,6 +1082,11 @@ AttachSelectorResult AttachSelectorToMenu(
Fn<void(ChosenReaction)> chosen,
Fn<void(FullMsgId)> showPremiumPromo,
IconFactory iconFactory) {
const auto settings = &AyuSettings::getInstance();
if (!AyuUi::needToShowItem(settings->showReactionsPanelInContextMenu)) {
return AttachSelectorResult::Skipped;
}
const auto result = AttachSelectorToMenu(
menu,
desiredPosition,
@ -1117,6 +1132,11 @@ auto AttachSelectorToMenu(
const Data::PossibleItemReactionsRef &reactions,
IconFactory iconFactory)
-> base::expected<not_null<Selector*>, AttachSelectorResult> {
const auto settings = &AyuSettings::getInstance();
if (!AyuUi::needToShowItem(settings->showReactionsPanelInContextMenu)) {
return base::make_unexpected(AttachSelectorResult::Skipped);
}
if (reactions.recent.empty() && !reactions.morePremiumAvailable) {
return base::make_unexpected(AttachSelectorResult::Skipped);
}

View file

@ -88,10 +88,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QCoreApplication>
#include <QtCore/QMimeData>
// AyuGram includes
#include "ayu/utils/telegram_helpers.h"
enum StackItemType {
HistoryStackItem,
SectionStackItem,