mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Allow sending photo/video captions above media.
This commit is contained in:
parent
924d80ecba
commit
67f7816088
15 changed files with 182 additions and 46 deletions
|
@ -251,6 +251,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_caption_limit2#other" = "Make the caption shorter or subscribe to **Telegram Premium** to double the limit to **{count}** characters.";
|
||||
"lng_caption_limit_reached#one" = "You've reached the media caption limit. Please make the caption shorter by {count} character.";
|
||||
"lng_caption_limit_reached#other" = "You've reached the media caption limit. Please make the caption shorter by {count} characters.";
|
||||
"lng_caption_move_up" = "Move Caption Up";
|
||||
"lng_caption_move_down" = "Move Caption Down";
|
||||
|
||||
"lng_file_size_limit_title" = "File Too Large";
|
||||
"lng_file_size_limit#one" = "{count} Gb";
|
||||
|
|
|
@ -26,6 +26,7 @@ struct SendOptions {
|
|||
EffectId effectId = 0;
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
bool invertCaption = false;
|
||||
bool hideViaBot = false;
|
||||
crl::time ttlSeconds = 0;
|
||||
};
|
||||
|
|
|
@ -81,7 +81,8 @@ mtpRequestId EditMessage(
|
|||
| ((!webpage.removed && !webpage.url.isEmpty())
|
||||
? MTPmessages_EditMessage::Flag::f_media
|
||||
: emptyFlag)
|
||||
| ((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
|
||||
| (((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
|
||||
|| options.invertCaption)
|
||||
? MTPmessages_EditMessage::Flag::f_invert_media
|
||||
: emptyFlag)
|
||||
| (!sentEntities.v.isEmpty()
|
||||
|
|
|
@ -136,6 +136,10 @@ void SendExistingMedia(
|
|||
if (action.options.effectId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||
}
|
||||
if (action.options.invertCaption) {
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
|
@ -314,6 +318,10 @@ bool SendDice(MessageToSend &message) {
|
|||
if (action.options.effectId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||
}
|
||||
if (action.options.invertCaption) {
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
|
@ -440,6 +448,9 @@ void SendConfirmedFile(
|
|||
flags |= MessageFlag::MediaIsUnread;
|
||||
}
|
||||
}
|
||||
if (file->to.options.invertCaption) {
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
}
|
||||
|
||||
const auto messageFromId = file->to.options.sendAs
|
||||
? file->to.options.sendAs->id
|
||||
|
|
|
@ -3772,7 +3772,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (exactWebPage && !ignoreWebPage && message.webPage.invert) {
|
||||
if ((exactWebPage && !ignoreWebPage && message.webPage.invert)
|
||||
|| action.options.invertCaption) {
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_invert_media;
|
||||
mediaFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
|
@ -4170,7 +4171,8 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
| (options.scheduled ? Flag::f_schedule_date : Flag(0))
|
||||
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
||||
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
||||
| (options.effectId ? Flag::f_effect : Flag(0));
|
||||
| (options.effectId ? Flag::f_effect : Flag(0))
|
||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0));
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
|
@ -4280,7 +4282,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
|||
| (album->options.shortcutId
|
||||
? Flag::f_quick_reply_shortcut
|
||||
: Flag(0))
|
||||
| (album->options.effectId ? Flag::f_effect : Flag(0));
|
||||
| (album->options.effectId ? Flag::f_effect : Flag(0))
|
||||
| (album->options.invertCaption ? Flag::f_invert_media : Flag(0));
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
histories.sendPreparedMessage(
|
||||
|
|
|
@ -350,9 +350,8 @@ SendFilesBox::SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor)
|
|||
, _titleHeight(st::boxTitleHeight)
|
||||
, _list(std::move(descriptor.list))
|
||||
, _limits(descriptor.limits)
|
||||
, _sendMenuDetails(descriptor.sendMenuDetails
|
||||
? descriptor.sendMenuDetails
|
||||
: [] { return SendMenu::Details(); })
|
||||
, _sendMenuDetails(prepareSendMenuDetails(descriptor))
|
||||
, _sendMenuCallback(prepareSendMenuCallback())
|
||||
, _captionToPeer(descriptor.captionToPeer)
|
||||
, _check(std::move(descriptor.check))
|
||||
, _confirmedCallback(std::move(descriptor.confirmed))
|
||||
|
@ -366,6 +365,50 @@ SendFilesBox::SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor)
|
|||
enqueueNextPrepare();
|
||||
}
|
||||
|
||||
Fn<SendMenu::Details()> SendFilesBox::prepareSendMenuDetails(
|
||||
const SendFilesBoxDescriptor &descriptor) {
|
||||
auto initial = descriptor.sendMenuDetails;
|
||||
return crl::guard(this, [=] {
|
||||
auto result = initial ? initial() : SendMenu::Details();
|
||||
result.spoiler = !hasSpoilerMenu()
|
||||
? SendMenu::SpoilerState::None
|
||||
: allWithSpoilers()
|
||||
? SendMenu::SpoilerState::Enabled
|
||||
: SendMenu::SpoilerState::Possible;
|
||||
const auto way = _sendWay.current();
|
||||
const auto canMoveCaption = _list.canMoveCaption(
|
||||
way.groupFiles() && way.sendImagesAsPhotos(),
|
||||
way.sendImagesAsPhotos()
|
||||
) && _caption && !_caption->getLastText().isEmpty();
|
||||
result.caption = !canMoveCaption
|
||||
? SendMenu::CaptionState::None
|
||||
: _invertCaption
|
||||
? SendMenu::CaptionState::Above
|
||||
: SendMenu::CaptionState::Below;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
auto SendFilesBox::prepareSendMenuCallback()
|
||||
-> Fn<void(MenuAction, MenuDetails)> {
|
||||
return crl::guard(this, [=](MenuAction action, MenuDetails details) {
|
||||
using Type = SendMenu::ActionType;
|
||||
switch (action.type) {
|
||||
case Type::CaptionDown: _invertCaption = false; break;
|
||||
case Type::CaptionUp: _invertCaption = true; break;
|
||||
case Type::SpoilerOn: toggleSpoilers(true); break;
|
||||
case Type::SpoilerOff: toggleSpoilers(false); break;
|
||||
default:
|
||||
SendMenu::DefaultCallback(
|
||||
_show,
|
||||
sendCallback())(
|
||||
action,
|
||||
details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SendFilesBox::initPreview() {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
|
@ -533,7 +576,7 @@ void SendFilesBox::refreshButtons() {
|
|||
_send,
|
||||
_show,
|
||||
_sendMenuDetails,
|
||||
SendMenu::DefaultCallback(_show, sendCallback()));
|
||||
_sendMenuCallback);
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
_addFile = addLeftButton(
|
||||
|
@ -545,8 +588,10 @@ void SendFilesBox::refreshButtons() {
|
|||
addMenuButton();
|
||||
}
|
||||
|
||||
bool SendFilesBox::hasSendMenu() const {
|
||||
return (_sendMenuDetails().type != SendMenu::Type::Disabled);
|
||||
bool SendFilesBox::hasSendMenu(const SendMenu::Details &details) const {
|
||||
return (details.type != SendMenu::Type::Disabled)
|
||||
|| (details.spoiler != SendMenu::SpoilerState::None)
|
||||
|| (details.caption != SendMenu::CaptionState::None);
|
||||
}
|
||||
|
||||
bool SendFilesBox::hasSpoilerMenu() const {
|
||||
|
@ -583,7 +628,8 @@ void SendFilesBox::toggleSpoilers(bool enabled) {
|
|||
}
|
||||
|
||||
void SendFilesBox::addMenuButton() {
|
||||
if (!hasSendMenu() && !hasSpoilerMenu()) {
|
||||
const auto details = _sendMenuDetails();
|
||||
if (!hasSendMenu(details)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -592,31 +638,16 @@ void SendFilesBox::addMenuButton() {
|
|||
const auto &tabbed = _st.tabbed;
|
||||
const auto &icons = tabbed.icons;
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(top, tabbed.menu);
|
||||
if (hasSpoilerMenu()) {
|
||||
const auto spoilered = allWithSpoilers();
|
||||
_menu->addAction(
|
||||
(spoilered
|
||||
? tr::lng_context_disable_spoiler(tr::now)
|
||||
: tr::lng_context_spoiler_effect(tr::now)),
|
||||
[=] { toggleSpoilers(!spoilered); },
|
||||
spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler);
|
||||
if (hasSendMenu()) {
|
||||
_menu->addSeparator(&tabbed.expandedSeparator);
|
||||
}
|
||||
}
|
||||
if (hasSendMenu()) {
|
||||
SendMenu::FillSendMenu(
|
||||
_menu.get(),
|
||||
_show,
|
||||
_sendMenuDetails(),
|
||||
SendMenu::DefaultCallback(_show, sendCallback()),
|
||||
&_st.tabbed.icons,
|
||||
QCursor::pos());
|
||||
}
|
||||
SendMenu::FillSendMenu(
|
||||
_menu.get(),
|
||||
_show,
|
||||
_sendMenuDetails(),
|
||||
_sendMenuCallback,
|
||||
&_st.tabbed.icons,
|
||||
QCursor::pos());
|
||||
_menu->popup(QCursor::pos());
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void SendFilesBox::initSendWay() {
|
||||
|
@ -658,9 +689,7 @@ void SendFilesBox::initSendWay() {
|
|||
for (auto &block : _blocks) {
|
||||
block.setSendWay(value);
|
||||
}
|
||||
if (!hasSendMenu()) {
|
||||
refreshButtons();
|
||||
}
|
||||
refreshButtons();
|
||||
if (was != hidden()) {
|
||||
updateBoxSize();
|
||||
updateControlsGeometry();
|
||||
|
@ -872,9 +901,7 @@ void SendFilesBox::pushBlock(int from, int till) {
|
|||
}
|
||||
|
||||
void SendFilesBox::refreshControls(bool initial) {
|
||||
if (initial || !hasSendMenu()) {
|
||||
refreshButtons();
|
||||
}
|
||||
refreshButtons();
|
||||
refreshTitleText();
|
||||
updateSendWayControls();
|
||||
updateCaptionPlaceholder();
|
||||
|
@ -1426,9 +1453,12 @@ void SendFilesBox::send(
|
|||
if ((_sendType == Api::SendType::Scheduled
|
||||
|| _sendType == Api::SendType::ScheduledToUser)
|
||||
&& !options.scheduled) {
|
||||
auto child = _sendMenuDetails();
|
||||
child.spoiler = SendMenu::SpoilerState::None;
|
||||
child.caption = SendMenu::CaptionState::None;
|
||||
return SendMenu::DefaultCallback(_show, sendCallback())(
|
||||
{ .type = SendMenu::ActionType::Schedule },
|
||||
_sendMenuDetails());
|
||||
child);
|
||||
}
|
||||
if (_preparing) {
|
||||
_whenReadySend = [=] {
|
||||
|
@ -1453,6 +1483,7 @@ void SendFilesBox::send(
|
|||
auto caption = (_caption && !_caption->isHidden())
|
||||
? _caption->getTextWithAppliedMarkdown()
|
||||
: TextWithTags();
|
||||
options.invertCaption = _invertCaption;
|
||||
if (!validateLength(caption.text)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class SessionController;
|
|||
|
||||
namespace SendMenu {
|
||||
struct Details;
|
||||
struct Action;
|
||||
} // namespace SendMenu
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
|
@ -136,6 +137,9 @@ protected:
|
|||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
using MenuAction = SendMenu::Action;
|
||||
using MenuDetails = SendMenu::Details;
|
||||
|
||||
class Block final {
|
||||
public:
|
||||
Block(
|
||||
|
@ -173,7 +177,7 @@ private:
|
|||
|
||||
void initSendWay();
|
||||
void initPreview();
|
||||
[[nodiscard]] bool hasSendMenu() const;
|
||||
[[nodiscard]] bool hasSendMenu(const MenuDetails &details) const;
|
||||
[[nodiscard]] bool hasSpoilerMenu() const;
|
||||
[[nodiscard]] bool allWithSpoilers();
|
||||
[[nodiscard]] bool checkWithWay(
|
||||
|
@ -225,6 +229,11 @@ private:
|
|||
|
||||
void checkCharsLimitation();
|
||||
|
||||
[[nodiscard]] Fn<MenuDetails()> prepareSendMenuDetails(
|
||||
const SendFilesBoxDescriptor &descriptor);
|
||||
[[nodiscard]] auto prepareSendMenuCallback()
|
||||
-> Fn<void(MenuAction, MenuDetails)>;
|
||||
|
||||
const std::shared_ptr<ChatHelpers::Show> _show;
|
||||
const style::ComposeControls &_st;
|
||||
const Api::SendType _sendType = Api::SendType();
|
||||
|
@ -236,12 +245,14 @@ private:
|
|||
std::optional<int> _removingIndex;
|
||||
|
||||
SendFilesLimits _limits = {};
|
||||
Fn<SendMenu::Details()> _sendMenuDetails = nullptr;
|
||||
Fn<MenuDetails()> _sendMenuDetails;
|
||||
Fn<void(MenuAction, MenuDetails)> _sendMenuCallback;
|
||||
PeerData *_captionToPeer = nullptr;
|
||||
SendFilesCheck _check;
|
||||
SendFilesConfirmed _confirmedCallback;
|
||||
Fn<void()> _cancelledCallback;
|
||||
bool _confirmed = false;
|
||||
bool _invertCaption = false;
|
||||
|
||||
object_ptr<Ui::InputField> _caption = { nullptr };
|
||||
TextWithTags _prefilledCaptionText;
|
||||
|
|
|
@ -69,6 +69,8 @@ ComposeIcons {
|
|||
menuWhenOnline: icon;
|
||||
menuSpoiler: icon;
|
||||
menuSpoilerOff: icon;
|
||||
menuBelow: icon;
|
||||
menuAbove: icon;
|
||||
|
||||
stripBubble: icon;
|
||||
stripExpandPanel: icon;
|
||||
|
@ -606,6 +608,8 @@ defaultComposeIcons: ComposeIcons {
|
|||
menuWhenOnline: menuIconWhenOnline;
|
||||
menuSpoiler: menuIconSpoiler;
|
||||
menuSpoilerOff: menuIconSpoilerOff;
|
||||
menuBelow: menuIconBelow;
|
||||
menuAbove: menuIconAbove;
|
||||
|
||||
stripBubble: icon{
|
||||
{ "chat/reactions_bubble_shadow", windowShadowFg },
|
||||
|
|
|
@ -1104,6 +1104,7 @@ void Reactions::defaultUpdated() {
|
|||
}
|
||||
refreshMyTags();
|
||||
refreshTags();
|
||||
refreshEffects();
|
||||
_defaultUpdated.fire({});
|
||||
}
|
||||
|
||||
|
|
|
@ -141,6 +141,9 @@ namespace Media::Stories {
|
|||
if (options.effectId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||
}
|
||||
if (options.invertCaption) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
}
|
||||
const auto done = [=] {
|
||||
if (!--state->requests) {
|
||||
if (show->valid()) {
|
||||
|
|
|
@ -636,6 +636,8 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
|
|||
menuWhenOnline: icon {{ "menu/send_when_online", storiesComposeWhiteText }};
|
||||
menuSpoiler: icon {{ "menu/spoiler_on", storiesComposeWhiteText }};
|
||||
menuSpoilerOff: icon {{ "menu/spoiler_off", storiesComposeWhiteText }};
|
||||
menuBelow: icon {{ "menu/link_below", storiesComposeWhiteText }};
|
||||
menuAbove: icon {{ "menu/link_above", storiesComposeWhiteText }};
|
||||
|
||||
stripBubble: icon{
|
||||
{ "chat/reactions_bubble_shadow", windowShadowFg },
|
||||
|
|
|
@ -615,13 +615,47 @@ FillMenuResult FillSendMenu(
|
|||
const style::ComposeIcons *iconsOverride,
|
||||
std::optional<QPoint> desiredPositionOverride) {
|
||||
const auto type = details.type;
|
||||
if (type == Type::Disabled || !action) {
|
||||
const auto empty = (type == Type::Disabled)
|
||||
&& (details.spoiler == SpoilerState::None)
|
||||
&& (details.caption == CaptionState::None);
|
||||
if (empty || !action) {
|
||||
return FillMenuResult::Skipped;
|
||||
}
|
||||
const auto &icons = iconsOverride
|
||||
? *iconsOverride
|
||||
: st::defaultComposeIcons;
|
||||
|
||||
auto toggles = false;
|
||||
if (details.spoiler != SpoilerState::None) {
|
||||
const auto spoilered = (details.spoiler == SpoilerState::Enabled);
|
||||
menu->addAction(
|
||||
(spoilered
|
||||
? tr::lng_context_disable_spoiler(tr::now)
|
||||
: tr::lng_context_spoiler_effect(tr::now)),
|
||||
[=] { action({ .type = spoilered
|
||||
? ActionType::SpoilerOff
|
||||
: ActionType::SpoilerOn
|
||||
}, details); },
|
||||
spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler);
|
||||
toggles = true;
|
||||
}
|
||||
if (details.caption != CaptionState::None) {
|
||||
const auto above = (details.caption == CaptionState::Above);
|
||||
menu->addAction(
|
||||
(above
|
||||
? tr::lng_caption_move_down(tr::now)
|
||||
: tr::lng_caption_move_up(tr::now)),
|
||||
[=] { action({ .type = above
|
||||
? ActionType::CaptionDown
|
||||
: ActionType::CaptionUp
|
||||
}, details); },
|
||||
above ? &icons.menuBelow : &icons.menuAbove);
|
||||
toggles = true;
|
||||
}
|
||||
if (toggles && type != Type::Disabled) {
|
||||
menu->addSeparator();
|
||||
}
|
||||
|
||||
if (type != Type::Reminder) {
|
||||
menu->addAction(
|
||||
tr::lng_send_silent_message(tr::now),
|
||||
|
|
|
@ -29,7 +29,7 @@ class Thread;
|
|||
|
||||
namespace SendMenu {
|
||||
|
||||
enum class Type {
|
||||
enum class Type : uchar {
|
||||
Disabled,
|
||||
SilentOnly,
|
||||
Scheduled,
|
||||
|
@ -37,20 +37,38 @@ enum class Type {
|
|||
Reminder,
|
||||
};
|
||||
|
||||
enum class SpoilerState : uchar {
|
||||
None,
|
||||
Enabled,
|
||||
Possible,
|
||||
};
|
||||
|
||||
enum class CaptionState : uchar {
|
||||
None,
|
||||
Below,
|
||||
Above,
|
||||
};
|
||||
|
||||
struct Details {
|
||||
Type type = Type::Disabled;
|
||||
SpoilerState spoiler = SpoilerState::None;
|
||||
CaptionState caption = CaptionState::None;
|
||||
bool effectAllowed = false;
|
||||
};
|
||||
|
||||
enum class FillMenuResult {
|
||||
enum class FillMenuResult : uchar {
|
||||
Prepared,
|
||||
Skipped,
|
||||
Failed,
|
||||
};
|
||||
|
||||
enum class ActionType {
|
||||
enum class ActionType : uchar {
|
||||
Send,
|
||||
Schedule,
|
||||
SpoilerOn,
|
||||
SpoilerOff,
|
||||
CaptionUp,
|
||||
CaptionDown,
|
||||
};
|
||||
struct Action {
|
||||
using Type = ActionType;
|
||||
|
|
|
@ -184,6 +184,17 @@ bool PreparedList::canAddCaption(bool sendingAlbum, bool compress) const {
|
|||
return !hasFiles && !hasMusic && !hasNotGrouped;
|
||||
}
|
||||
|
||||
bool PreparedList::canMoveCaption(bool sendingAlbum, bool compress) const {
|
||||
if (!canAddCaption(sendingAlbum, compress)) {
|
||||
return false;
|
||||
} else if (files.size() != 1) {
|
||||
return true;
|
||||
}
|
||||
const auto &file = files.front();
|
||||
return (file.type == PreparedFile::Type::Video)
|
||||
|| (file.type == PreparedFile::Type::Photo && compress);
|
||||
}
|
||||
|
||||
bool PreparedList::hasGroupOption(bool slowmode) const {
|
||||
if (slowmode || files.size() < 2) {
|
||||
return false;
|
||||
|
|
|
@ -112,6 +112,9 @@ struct PreparedList {
|
|||
void mergeToEnd(PreparedList &&other, bool cutToAlbumSize = false);
|
||||
|
||||
[[nodiscard]] bool canAddCaption(bool sendingAlbum, bool compress) const;
|
||||
[[nodiscard]] bool canMoveCaption(
|
||||
bool sendingAlbum,
|
||||
bool compress) const;
|
||||
[[nodiscard]] bool canBeSentInSlowmode() const;
|
||||
[[nodiscard]] bool canBeSentInSlowmodeWith(
|
||||
const PreparedList &other) const;
|
||||
|
|
Loading…
Add table
Reference in a new issue