Allow forward+reply, options in single box.
BIN
Telegram/Resources/icons/menu/caption_hide.png
Normal file
After Width: | Height: | Size: 718 B |
BIN
Telegram/Resources/icons/menu/caption_hide@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/menu/caption_hide@3x.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/menu/caption_show.png
Normal file
After Width: | Height: | Size: 505 B |
BIN
Telegram/Resources/icons/menu/caption_show@2x.png
Normal file
After Width: | Height: | Size: 763 B |
BIN
Telegram/Resources/icons/menu/caption_show@3x.png
Normal file
After Width: | Height: | Size: 963 B |
BIN
Telegram/Resources/icons/menu/name_hide.png
Normal file
After Width: | Height: | Size: 615 B |
BIN
Telegram/Resources/icons/menu/name_hide@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/menu/name_hide@3x.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/menu/name_show.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
Telegram/Resources/icons/menu/name_show@2x.png
Normal file
After Width: | Height: | Size: 959 B |
BIN
Telegram/Resources/icons/menu/name_show@3x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
|
@ -4912,6 +4912,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_forward_show_captions" = "Show captions";
|
"lng_forward_show_captions" = "Show captions";
|
||||||
"lng_forward_change_recipient" = "Change recipient";
|
"lng_forward_change_recipient" = "Change recipient";
|
||||||
"lng_forward_sender_names_removed" = "Sender names removed";
|
"lng_forward_sender_names_removed" = "Sender names removed";
|
||||||
|
"lng_forward_header_short" = "Forward";
|
||||||
|
"lng_forward_action_show_sender" = "Show Sender Name";
|
||||||
|
"lng_forward_action_show_senders" = "Show Sender Names";
|
||||||
|
"lng_forward_action_hide_sender" = "Hide Sender Name";
|
||||||
|
"lng_forward_action_hide_senders" = "Hide Sender Names";
|
||||||
|
"lng_forward_action_show_caption" = "Show Caption";
|
||||||
|
"lng_forward_action_show_captions" = "Show Captions";
|
||||||
|
"lng_forward_action_hide_caption" = "Hide Caption";
|
||||||
|
"lng_forward_action_hide_captions" = "Hide Captions";
|
||||||
|
"lng_forward_action_change_recipient" = "Change Recipient";
|
||||||
|
"lng_forward_action_remove" = "Do Not Forward";
|
||||||
|
|
||||||
"lng_passport_title" = "Telegram Passport";
|
"lng_passport_title" = "Telegram Passport";
|
||||||
"lng_passport_request1" = "{bot} requests access to your personal data";
|
"lng_passport_request1" = "{bot} requests access to your personal data";
|
||||||
|
|
|
@ -36,6 +36,8 @@ using Options = base::flags<Option>;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
|
struct FileOrigin;
|
||||||
|
|
||||||
struct UploadState {
|
struct UploadState {
|
||||||
explicit UploadState(int64 size) : size(size) {
|
explicit UploadState(int64 size) : size(size) {
|
||||||
}
|
}
|
||||||
|
@ -58,8 +60,6 @@ constexpr auto kVoiceMessageCacheTag = uint8(0x03);
|
||||||
constexpr auto kVideoMessageCacheTag = uint8(0x04);
|
constexpr auto kVideoMessageCacheTag = uint8(0x04);
|
||||||
constexpr auto kAnimationCacheTag = uint8(0x05);
|
constexpr auto kAnimationCacheTag = uint8(0x05);
|
||||||
|
|
||||||
struct FileOrigin;
|
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
struct MessageGroupId {
|
struct MessageGroupId {
|
||||||
|
@ -342,3 +342,29 @@ enum class MediaWebPageFlag : uint8 {
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(MediaWebPageFlag) { return true; }
|
inline constexpr bool is_flag_type(MediaWebPageFlag) { return true; }
|
||||||
using MediaWebPageFlags = base::flags<MediaWebPageFlag>;
|
using MediaWebPageFlags = base::flags<MediaWebPageFlag>;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
enum class ForwardOptions {
|
||||||
|
PreserveInfo,
|
||||||
|
NoSenderNames,
|
||||||
|
NoNamesAndCaptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ForwardDraft {
|
||||||
|
MessageIdsList ids;
|
||||||
|
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||||
|
|
||||||
|
friend inline auto operator<=>(
|
||||||
|
const ForwardDraft&,
|
||||||
|
const ForwardDraft&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ForwardDrafts = base::flat_map<MsgId, ForwardDraft>;
|
||||||
|
|
||||||
|
struct ResolvedForwardDraft {
|
||||||
|
HistoryItemsList items;
|
||||||
|
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
||||||
|
|
|
@ -26,7 +26,6 @@ class HistoryMainElementDelegateMixin;
|
||||||
struct LanguageId;
|
struct LanguageId;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
struct Draft;
|
struct Draft;
|
||||||
class Session;
|
class Session;
|
||||||
class Folder;
|
class Folder;
|
||||||
|
@ -34,29 +33,6 @@ class ChatFilter;
|
||||||
struct SponsoredFrom;
|
struct SponsoredFrom;
|
||||||
class SponsoredMessages;
|
class SponsoredMessages;
|
||||||
class HistoryMessages;
|
class HistoryMessages;
|
||||||
|
|
||||||
enum class ForwardOptions {
|
|
||||||
PreserveInfo,
|
|
||||||
NoSenderNames,
|
|
||||||
NoNamesAndCaptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ForwardDraft {
|
|
||||||
MessageIdsList ids;
|
|
||||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
|
||||||
|
|
||||||
friend inline auto operator<=>(
|
|
||||||
const ForwardDraft&,
|
|
||||||
const ForwardDraft&) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ForwardDrafts = base::flat_map<MsgId, ForwardDraft>;
|
|
||||||
|
|
||||||
struct ResolvedForwardDraft {
|
|
||||||
HistoryItemsList items;
|
|
||||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
|
|
@ -136,6 +136,9 @@ template <typename T>
|
||||||
HistoryItemCommonFields fields,
|
HistoryItemCommonFields fields,
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
not_null<HistoryItem*> original) {
|
not_null<HistoryItem*> original) {
|
||||||
|
if (fields.flags & MessageFlag::FakeHistoryItem) {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
fields.flags |= NewForwardedFlags(history->peer, fields.from, original);
|
fields.flags |= NewForwardedFlags(history->peer, fields.from, original);
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
@ -509,7 +512,8 @@ HistoryItem::HistoryItem(
|
||||||
auto config = CreateConfig();
|
auto config = CreateConfig();
|
||||||
|
|
||||||
const auto originalMedia = original->media();
|
const auto originalMedia = original->media();
|
||||||
const auto dropForwardInfo = original->computeDropForwardedInfo();
|
const auto dropForwardInfo = fields.ignoreForwardFrom
|
||||||
|
|| original->computeDropForwardedInfo();
|
||||||
const auto topicRootId = fields.replyTo.topicRootId;
|
const auto topicRootId = fields.replyTo.topicRootId;
|
||||||
config.reply.messageId = config.reply.topMessageId = topicRootId;
|
config.reply.messageId = config.reply.topMessageId = topicRootId;
|
||||||
config.reply.topicPost = (topicRootId != 0) ? 1 : 0;
|
config.reply.topicPost = (topicRootId != 0) ? 1 : 0;
|
||||||
|
@ -597,9 +601,22 @@ HistoryItem::HistoryItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setText(dropForwardInfo
|
const auto dropText = fields.ignoreForwardCaptions
|
||||||
|
&& _media
|
||||||
|
&& (_media->photo() || _media->document())
|
||||||
|
&& !_media->webpage();
|
||||||
|
setText(dropText
|
||||||
|
? TextWithEntities()
|
||||||
|
: dropForwardInfo
|
||||||
? DropDisallowedCustomEmoji(history->peer, original->originalText())
|
? DropDisallowedCustomEmoji(history->peer, original->originalText())
|
||||||
: original->originalText());
|
: original->originalText());
|
||||||
|
|
||||||
|
if (fields.groupedId) {
|
||||||
|
setGroupId(MessageGroupId::FromRaw(
|
||||||
|
history->peer->id,
|
||||||
|
fields.groupedId,
|
||||||
|
_flags & MessageFlag::IsOrWasScheduled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem::HistoryItem(
|
HistoryItem::HistoryItem(
|
||||||
|
|
|
@ -107,6 +107,8 @@ struct HistoryItemCommonFields {
|
||||||
uint64 groupedId = 0;
|
uint64 groupedId = 0;
|
||||||
EffectId effectId = 0;
|
EffectId effectId = 0;
|
||||||
HistoryMessageMarkupData markup;
|
HistoryMessageMarkupData markup;
|
||||||
|
bool ignoreForwardFrom = false;
|
||||||
|
bool ignoreForwardCaptions = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class HistoryReactionSource : char {
|
enum class HistoryReactionSource : char {
|
||||||
|
|
|
@ -2109,7 +2109,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
||||||
if (!_replyEditMsg) {
|
if (!_replyEditMsg) {
|
||||||
requestMessageData(_editMsgId);
|
requestMessageData(_editMsgId);
|
||||||
}
|
}
|
||||||
} else if (!readyToForward()) {
|
} else {
|
||||||
const auto draft = _history->localDraft({});
|
const auto draft = _history->localDraft({});
|
||||||
_processingReplyTo = draft ? draft->reply : FullReplyTo();
|
_processingReplyTo = draft ? draft->reply : FullReplyTo();
|
||||||
if (_processingReplyTo) {
|
if (_processingReplyTo) {
|
||||||
|
@ -4982,7 +4982,7 @@ bool HistoryWidget::showRecordButton() const {
|
||||||
&& !_voiceRecordBar->isRecordingByAnotherBar()
|
&& !_voiceRecordBar->isRecordingByAnotherBar()
|
||||||
&& !HasSendText(_field)
|
&& !HasSendText(_field)
|
||||||
&& !_previewDrawPreview
|
&& !_previewDrawPreview
|
||||||
&& !readyToForward()
|
&& (_replyTo || !readyToForward())
|
||||||
&& !_editMsgId;
|
&& !_editMsgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6825,18 +6825,15 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
_peer,
|
_peer,
|
||||||
Window::SectionShow::Way::Forward,
|
Window::SectionShow::Way::Forward,
|
||||||
_editMsgId);
|
_editMsgId);
|
||||||
} else if (isReadyToForward) {
|
|
||||||
if (e->button() != Qt::LeftButton) {
|
|
||||||
_forwardPanel->editToNextOption();
|
|
||||||
} else {
|
|
||||||
_forwardPanel->editOptions(controller()->uiShow());
|
|
||||||
}
|
|
||||||
} else if (_replyTo
|
} else if (_replyTo
|
||||||
&& ((e->modifiers() & Qt::ControlModifier)
|
&& ((e->modifiers() & Qt::ControlModifier)
|
||||||
|| (e->button() != Qt::LeftButton))) {
|
|| (e->button() != Qt::LeftButton))) {
|
||||||
jumpToReply(_replyTo);
|
jumpToReply(_replyTo);
|
||||||
} else if (_replyTo) {
|
} else if (_replyTo
|
||||||
|
|| (isReadyToForward && e->button() == Qt::LeftButton)) {
|
||||||
editDraftOptions();
|
editDraftOptions();
|
||||||
|
} else if (isReadyToForward) {
|
||||||
|
_forwardPanel->editToNextOption();
|
||||||
} else if (_kbReplyTo) {
|
} else if (_kbReplyTo) {
|
||||||
controller()->showPeerHistory(
|
controller()->showPeerHistory(
|
||||||
_kbReplyTo->history()->peer->id,
|
_kbReplyTo->history()->peer->id,
|
||||||
|
@ -6851,15 +6848,18 @@ void HistoryWidget::editDraftOptions() {
|
||||||
const auto history = _history;
|
const auto history = _history;
|
||||||
const auto reply = _replyTo;
|
const auto reply = _replyTo;
|
||||||
const auto webpage = _preview->draft();
|
const auto webpage = _preview->draft();
|
||||||
|
const auto forward = _forwardPanel->draft();
|
||||||
|
|
||||||
const auto done = [=](
|
const auto done = [=](
|
||||||
FullReplyTo replyTo,
|
FullReplyTo replyTo,
|
||||||
Data::WebPageDraft webpage) {
|
Data::WebPageDraft webpage,
|
||||||
|
Data::ForwardDraft forward) {
|
||||||
if (replyTo) {
|
if (replyTo) {
|
||||||
replyToMessage(replyTo);
|
replyToMessage(replyTo);
|
||||||
} else {
|
} else {
|
||||||
cancelReply();
|
cancelReply();
|
||||||
}
|
}
|
||||||
|
history->setForwardDraft({}, std::move(forward));
|
||||||
_preview->apply(webpage);
|
_preview->apply(webpage);
|
||||||
};
|
};
|
||||||
const auto replyToId = reply.messageId;
|
const auto replyToId = reply.messageId;
|
||||||
|
@ -6873,6 +6873,7 @@ void HistoryWidget::editDraftOptions() {
|
||||||
.history = history,
|
.history = history,
|
||||||
.draft = Data::Draft(_field, reply, _preview->draft()),
|
.draft = Data::Draft(_field, reply, _preview->draft()),
|
||||||
.usedLink = _preview->link(),
|
.usedLink = _preview->link(),
|
||||||
|
.forward = _forwardPanel->draft(),
|
||||||
.links = _preview->links(),
|
.links = _preview->links(),
|
||||||
.resolver = _preview->resolver(),
|
.resolver = _preview->resolver(),
|
||||||
.done = done,
|
.done = done,
|
||||||
|
@ -7956,7 +7957,7 @@ void HistoryWidget::processReply() {
|
||||||
{ .ids = { 1, itemId } });
|
{ .ids = { 1, itemId } });
|
||||||
}),
|
}),
|
||||||
.confirmText = tr::lng_selected_forward(),
|
.confirmText = tr::lng_selected_forward(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return processCancel();
|
return processCancel();
|
||||||
#endif
|
#endif
|
||||||
|
@ -7985,7 +7986,6 @@ void HistoryWidget::setReplyFieldsFromProcessing() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_history->setForwardDraft(MsgId(), {});
|
|
||||||
if (_composeSearch) {
|
if (_composeSearch) {
|
||||||
_composeSearch->hideAnimated();
|
_composeSearch->hideAnimated();
|
||||||
}
|
}
|
||||||
|
@ -8243,10 +8243,10 @@ void HistoryWidget::cancelFieldAreaState() {
|
||||||
_preview->apply({ .removed = true });
|
_preview->apply({ .removed = true });
|
||||||
} else if (_editMsgId) {
|
} else if (_editMsgId) {
|
||||||
cancelEdit();
|
cancelEdit();
|
||||||
} else if (readyToForward()) {
|
|
||||||
_history->setForwardDraft(MsgId(), {});
|
|
||||||
} else if (_replyTo) {
|
} else if (_replyTo) {
|
||||||
cancelReply();
|
cancelReply();
|
||||||
|
} else if (readyToForward()) {
|
||||||
|
_history->setForwardDraft(MsgId(), {});
|
||||||
} else if (_kbReplyTo) {
|
} else if (_kbReplyTo) {
|
||||||
toggleKeyboard();
|
toggleKeyboard();
|
||||||
}
|
}
|
||||||
|
@ -8595,9 +8595,6 @@ void HistoryWidget::updateForwarding() {
|
||||||
_forwardPanel->update(_history, _history
|
_forwardPanel->update(_history, _history
|
||||||
? _history->resolveForwardDraft(MsgId())
|
? _history->resolveForwardDraft(MsgId())
|
||||||
: Data::ResolvedForwardDraft());
|
: Data::ResolvedForwardDraft());
|
||||||
if (readyToForward()) {
|
|
||||||
cancelReply();
|
|
||||||
}
|
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@ public:
|
||||||
[[nodiscard]] bool isEditingMessage() const;
|
[[nodiscard]] bool isEditingMessage() const;
|
||||||
[[nodiscard]] bool readyToForward() const;
|
[[nodiscard]] bool readyToForward() const;
|
||||||
[[nodiscard]] const HistoryItemsList &forwardItems() const;
|
[[nodiscard]] const HistoryItemsList &forwardItems() const;
|
||||||
|
[[nodiscard]] const Data::ResolvedForwardDraft &forwardDraft() const;
|
||||||
[[nodiscard]] FullReplyTo replyingToMessage() const;
|
[[nodiscard]] FullReplyTo replyingToMessage() const;
|
||||||
[[nodiscard]] FullMsgId editMsgId() const;
|
[[nodiscard]] FullMsgId editMsgId() const;
|
||||||
[[nodiscard]] rpl::producer<FullMsgId> editMsgIdValue() const;
|
[[nodiscard]] rpl::producer<FullMsgId> editMsgIdValue() const;
|
||||||
|
@ -281,23 +282,23 @@ void FieldHeader::init() {
|
||||||
st::historyLinkIcon.paint(p, position, width());
|
st::historyLinkIcon.paint(p, position, width());
|
||||||
} else if (isEditingMessage()) {
|
} else if (isEditingMessage()) {
|
||||||
st::historyEditIcon.paint(p, position, width());
|
st::historyEditIcon.paint(p, position, width());
|
||||||
} else if (readyToForward()) {
|
|
||||||
st::historyForwardIcon.paint(p, position, width());
|
|
||||||
} else if (const auto reply = replyingToMessage()) {
|
} else if (const auto reply = replyingToMessage()) {
|
||||||
if (!reply.quote.empty()) {
|
if (!reply.quote.empty()) {
|
||||||
st::historyQuoteIcon.paint(p, position, width());
|
st::historyQuoteIcon.paint(p, position, width());
|
||||||
} else {
|
} else {
|
||||||
st::historyReplyIcon.paint(p, position, width());
|
st::historyReplyIcon.paint(p, position, width());
|
||||||
}
|
}
|
||||||
|
} else if (readyToForward()) {
|
||||||
|
st::historyForwardIcon.paint(p, position, width());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_preview.parsed) {
|
if (_preview.parsed) {
|
||||||
paintWebPage(
|
paintWebPage(
|
||||||
p,
|
p,
|
||||||
_history ? _history->peer : _data->session().user());
|
_history ? _history->peer : _data->session().user());
|
||||||
} else if (isEditingMessage() || !readyToForward()) {
|
} else if (isEditingMessage() || replyingToMessage()) {
|
||||||
paintEditOrReplyToMessage(p);
|
paintEditOrReplyToMessage(p);
|
||||||
} else {
|
} else if (readyToForward()) {
|
||||||
paintForwardInfo(p);
|
paintForwardInfo(p);
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
@ -339,10 +340,10 @@ void FieldHeader::init() {
|
||||||
_previewCancelled.fire({});
|
_previewCancelled.fire({});
|
||||||
} else if (_editMsgId.current()) {
|
} else if (_editMsgId.current()) {
|
||||||
_editCancelled.fire({});
|
_editCancelled.fire({});
|
||||||
} else if (readyToForward()) {
|
|
||||||
_forwardCancelled.fire({});
|
|
||||||
} else if (_replyTo.current()) {
|
} else if (_replyTo.current()) {
|
||||||
_replyCancelled.fire({});
|
_replyCancelled.fire({});
|
||||||
|
} else if (readyToForward()) {
|
||||||
|
_forwardCancelled.fire({});
|
||||||
}
|
}
|
||||||
updateVisible();
|
updateVisible();
|
||||||
update();
|
update();
|
||||||
|
@ -403,12 +404,9 @@ void FieldHeader::init() {
|
||||||
_jumpToItemRequests.fire(FullReplyTo{
|
_jumpToItemRequests.fire(FullReplyTo{
|
||||||
.messageId = _editMsgId.current()
|
.messageId = _editMsgId.current()
|
||||||
});
|
});
|
||||||
} else if (readyToForward()) {
|
} else if (reply && (e->modifiers() & Qt::ControlModifier)) {
|
||||||
_forwardPanel->editOptions(_show);
|
|
||||||
} else if (reply
|
|
||||||
&& (e->modifiers() & Qt::ControlModifier)) {
|
|
||||||
_jumpToItemRequests.fire_copy(reply);
|
_jumpToItemRequests.fire_copy(reply);
|
||||||
} else if (reply) {
|
} else if (reply || readyToForward()) {
|
||||||
_editOptionsRequests.fire({});
|
_editOptionsRequests.fire({});
|
||||||
}
|
}
|
||||||
} else if (!isLeftButton) {
|
} else if (!isLeftButton) {
|
||||||
|
@ -419,6 +417,8 @@ void FieldHeader::init() {
|
||||||
_hasSendText());
|
_hasSendText());
|
||||||
} else if (const auto reply = replyingToMessage()) {
|
} else if (const auto reply = replyingToMessage()) {
|
||||||
_jumpToItemRequests.fire_copy(reply);
|
_jumpToItemRequests.fire_copy(reply);
|
||||||
|
} else if (readyToForward()) {
|
||||||
|
_forwardPanel->editToNextOption();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,6 +713,10 @@ const HistoryItemsList &FieldHeader::forwardItems() const {
|
||||||
return _forwardPanel->items();
|
return _forwardPanel->items();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Data::ResolvedForwardDraft &FieldHeader::forwardDraft() const {
|
||||||
|
return _forwardPanel->draft();
|
||||||
|
}
|
||||||
|
|
||||||
FullReplyTo FieldHeader::replyingToMessage() const {
|
FullReplyTo FieldHeader::replyingToMessage() const {
|
||||||
return _replyTo.current();
|
return _replyTo.current();
|
||||||
}
|
}
|
||||||
|
@ -764,9 +768,6 @@ void FieldHeader::updateForwarding(
|
||||||
Data::Thread *thread,
|
Data::Thread *thread,
|
||||||
Data::ResolvedForwardDraft items) {
|
Data::ResolvedForwardDraft items) {
|
||||||
_forwardPanel->update(thread, std::move(items));
|
_forwardPanel->update(thread, std::move(items));
|
||||||
if (readyToForward()) {
|
|
||||||
replyToMessage({});
|
|
||||||
}
|
|
||||||
updateControlsGeometry(size());
|
updateControlsGeometry(size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1408,12 +1409,14 @@ void ComposeControls::init() {
|
||||||
|
|
||||||
const auto done = [=](
|
const auto done = [=](
|
||||||
FullReplyTo replyTo,
|
FullReplyTo replyTo,
|
||||||
Data::WebPageDraft webpage) {
|
Data::WebPageDraft webpage,
|
||||||
|
Data::ForwardDraft forward) {
|
||||||
if (replyTo) {
|
if (replyTo) {
|
||||||
replyToMessage(replyTo);
|
replyToMessage(replyTo);
|
||||||
} else {
|
} else {
|
||||||
cancelReplyMessage();
|
cancelReplyMessage();
|
||||||
}
|
}
|
||||||
|
history->setForwardDraft(topicRootId, std::move(forward));
|
||||||
_preview->apply(webpage);
|
_preview->apply(webpage);
|
||||||
_field->setFocus();
|
_field->setFocus();
|
||||||
};
|
};
|
||||||
|
@ -1428,6 +1431,7 @@ void ComposeControls::init() {
|
||||||
.history = history,
|
.history = history,
|
||||||
.draft = Data::Draft(_field, reply, _preview->draft()),
|
.draft = Data::Draft(_field, reply, _preview->draft()),
|
||||||
.usedLink = _preview->link(),
|
.usedLink = _preview->link(),
|
||||||
|
.forward = _header->forwardDraft(),
|
||||||
.links = _preview->links(),
|
.links = _preview->links(),
|
||||||
.resolver = _preview->resolver(),
|
.resolver = _preview->resolver(),
|
||||||
.done = done,
|
.done = done,
|
||||||
|
@ -2013,9 +2017,6 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
||||||
_canReplaceMedia = _canAddMedia = false;
|
_canReplaceMedia = _canAddMedia = false;
|
||||||
_photoEditMedia = nullptr;
|
_photoEditMedia = nullptr;
|
||||||
_header->replyToMessage(draft->reply);
|
_header->replyToMessage(draft->reply);
|
||||||
if (_header->replyingToMessage()) {
|
|
||||||
cancelForward();
|
|
||||||
}
|
|
||||||
_header->editMessage({});
|
_header->editMessage({});
|
||||||
if (_preview) {
|
if (_preview) {
|
||||||
_preview->setDisabled(false);
|
_preview->setDisabled(false);
|
||||||
|
@ -3018,9 +3019,6 @@ void ComposeControls::replyToMessage(FullReplyTo id) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_header->replyToMessage(id);
|
_header->replyToMessage(id);
|
||||||
if (_header->replyingToMessage()) {
|
|
||||||
cancelForward();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveDraftText = true;
|
_saveDraftText = true;
|
||||||
|
@ -3071,12 +3069,12 @@ bool ComposeControls::handleCancelRequest() {
|
||||||
} else if (isEditingMessage()) {
|
} else if (isEditingMessage()) {
|
||||||
maybeCancelEditMessage();
|
maybeCancelEditMessage();
|
||||||
return true;
|
return true;
|
||||||
} else if (readyToForward()) {
|
|
||||||
cancelForward();
|
|
||||||
return true;
|
|
||||||
} else if (replyingToMessage()) {
|
} else if (replyingToMessage()) {
|
||||||
cancelReplyMessage();
|
cancelReplyMessage();
|
||||||
return true;
|
return true;
|
||||||
|
} else if (readyToForward()) {
|
||||||
|
cancelForward();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "history/view/controls/history_view_draft_options.h"
|
#include "history/view/controls/history_view_draft_options.h"
|
||||||
|
|
||||||
|
#include "base/random.h"
|
||||||
#include "base/timer_rpl.h"
|
#include "base/timer_rpl.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "boxes/filters/edit_filter_chats_list.h"
|
#include "boxes/filters/edit_filter_chats_list.h"
|
||||||
|
@ -20,12 +21,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_thread.h"
|
#include "data/data_thread.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
|
#include "history/view/controls/history_view_forward_panel.h"
|
||||||
#include "history/view/controls/history_view_webpage_processor.h"
|
#include "history/view/controls/history_view_webpage_processor.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item_helpers.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
@ -41,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "window/themes/window_theme.h"
|
#include "window/themes/window_theme.h"
|
||||||
#include "window/section_widget.h"
|
#include "window/section_widget.h"
|
||||||
|
#include "window/window_peer_menu.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
@ -56,6 +60,7 @@ namespace {
|
||||||
|
|
||||||
enum class Section {
|
enum class Section {
|
||||||
Reply,
|
Reply,
|
||||||
|
Forward,
|
||||||
Link,
|
Link,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,6 +109,10 @@ public:
|
||||||
not_null<History*> history);
|
not_null<History*> history);
|
||||||
~PreviewWrap();
|
~PreviewWrap();
|
||||||
|
|
||||||
|
[[nodiscard]] bool hasViewForItem(
|
||||||
|
not_null<const HistoryItem*> item) const;
|
||||||
|
|
||||||
|
void showForwardSelector(Data::ResolvedForwardDraft draft);
|
||||||
[[nodiscard]] rpl::producer<SelectedQuote> showQuoteSelector(
|
[[nodiscard]] rpl::producer<SelectedQuote> showQuoteSelector(
|
||||||
const SelectedQuote "e);
|
const SelectedQuote "e);
|
||||||
[[nodiscard]] rpl::producer<QString> showLinkSelector(
|
[[nodiscard]] rpl::producer<QString> showLinkSelector(
|
||||||
|
@ -117,6 +126,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Entry {
|
||||||
|
HistoryItem *item = nullptr;
|
||||||
|
std::unique_ptr<Element> view;
|
||||||
|
};
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void leaveEventHook(QEvent *e) override;
|
void leaveEventHook(QEvent *e) override;
|
||||||
void mouseMoveEvent(QMouseEvent *e) override;
|
void mouseMoveEvent(QMouseEvent *e) override;
|
||||||
|
@ -129,7 +143,8 @@ private:
|
||||||
_visibleBottom = bottom;
|
_visibleBottom = bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initElement();
|
void clear(std::vector<Entry> entries);
|
||||||
|
void initElements();
|
||||||
void highlightUsedLink(
|
void highlightUsedLink(
|
||||||
const TextWithTags &message,
|
const TextWithTags &message,
|
||||||
const QString &usedLink,
|
const QString &usedLink,
|
||||||
|
@ -144,8 +159,8 @@ private:
|
||||||
const std::unique_ptr<PreviewDelegate> _delegate;
|
const std::unique_ptr<PreviewDelegate> _delegate;
|
||||||
|
|
||||||
Section _section = Section::Reply;
|
Section _section = Section::Reply;
|
||||||
HistoryItem *_draftItem = nullptr;
|
std::vector<Entry> _entries;
|
||||||
std::unique_ptr<Element> _element;
|
base::flat_set<not_null<const Element*>> _views;
|
||||||
rpl::variable<TextSelection> _selection;
|
rpl::variable<TextSelection> _selection;
|
||||||
rpl::event_stream<QString> _chosenUrl;
|
rpl::event_stream<QString> _chosenUrl;
|
||||||
Ui::PeerUserpicView _userpic;
|
Ui::PeerUserpicView _userpic;
|
||||||
|
@ -191,7 +206,7 @@ PreviewWrap::PreviewWrap(
|
||||||
const auto session = &_history->session();
|
const auto session = &_history->session();
|
||||||
session->data().viewRepaintRequest(
|
session->data().viewRepaintRequest(
|
||||||
) | rpl::start_with_next([=](not_null<const Element*> view) {
|
) | rpl::start_with_next([=](not_null<const Element*> view) {
|
||||||
if (view == _element.get()) {
|
if (_views.contains(view)) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
@ -221,26 +236,92 @@ PreviewWrap::PreviewWrap(
|
||||||
|
|
||||||
PreviewWrap::~PreviewWrap() {
|
PreviewWrap::~PreviewWrap() {
|
||||||
_selection.reset(TextSelection());
|
_selection.reset(TextSelection());
|
||||||
|
base::take(_views);
|
||||||
|
clear(base::take(_entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::clear(std::vector<Entry> entries) {
|
||||||
_elementLifetime.destroy();
|
_elementLifetime.destroy();
|
||||||
_element = nullptr;
|
for (auto &entry : entries) {
|
||||||
if (_draftItem) {
|
entry.view = nullptr;
|
||||||
_draftItem->destroy();
|
if (const auto item = entry.item) {
|
||||||
|
item->destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PreviewWrap::hasViewForItem(not_null<const HistoryItem*> item) const {
|
||||||
|
return (item->history() == _history)
|
||||||
|
&& ranges::contains(_views, item, &Element::data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::showForwardSelector(Data::ResolvedForwardDraft draft) {
|
||||||
|
Expects(!draft.items.empty());
|
||||||
|
|
||||||
|
_selection.reset(TextSelection());
|
||||||
|
|
||||||
|
auto was = base::take(_entries);
|
||||||
|
auto groups = base::flat_map<MessageGroupId, uint64>();
|
||||||
|
const auto groupByItem = [&](not_null<HistoryItem*> item) {
|
||||||
|
const auto groupId = item->groupId();
|
||||||
|
if (!groupId) {
|
||||||
|
return uint64();
|
||||||
|
}
|
||||||
|
auto i = groups.find(groupId);
|
||||||
|
if (i == end(groups)) {
|
||||||
|
i = groups.emplace(groupId, base::RandomValue<uint64>()).first;
|
||||||
|
}
|
||||||
|
return i->second;
|
||||||
|
};
|
||||||
|
const auto wasViews = base::take(_views);
|
||||||
|
using Options = Data::ForwardOptions;
|
||||||
|
const auto dropNames = (draft.options != Options::PreserveInfo);
|
||||||
|
const auto dropCaptions = (draft.options == Options::NoNamesAndCaptions);
|
||||||
|
for (const auto &source : draft.items) {
|
||||||
|
const auto groupedId = groupByItem(source);
|
||||||
|
const auto item = _history->addNewLocalMessage({
|
||||||
|
.id = _history->nextNonHistoryEntryId(),
|
||||||
|
.flags = (MessageFlag::FakeHistoryItem
|
||||||
|
| MessageFlag::Outgoing
|
||||||
|
| MessageFlag::HasFromId
|
||||||
|
| (source->invertMedia()
|
||||||
|
? MessageFlag::InvertMedia
|
||||||
|
: MessageFlag())),
|
||||||
|
.from = _history->session().userPeerId(),
|
||||||
|
.date = base::unixtime::now(),
|
||||||
|
.groupedId = groupedId,
|
||||||
|
.ignoreForwardFrom = dropNames,
|
||||||
|
.ignoreForwardCaptions = dropCaptions,
|
||||||
|
}, source);
|
||||||
|
_entries.push_back({ item });
|
||||||
|
}
|
||||||
|
for (auto &entry : _entries) {
|
||||||
|
entry.view = entry.item->createView(_delegate.get());
|
||||||
|
_views.emplace(entry.view.get());
|
||||||
|
}
|
||||||
|
_link = _pressedLink = nullptr;
|
||||||
|
clear(std::move(was));
|
||||||
|
|
||||||
|
_section = Section::Forward;
|
||||||
|
|
||||||
|
initElements();
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<SelectedQuote> PreviewWrap::showQuoteSelector(
|
rpl::producer<SelectedQuote> PreviewWrap::showQuoteSelector(
|
||||||
const SelectedQuote "e) {
|
const SelectedQuote "e) {
|
||||||
_selection.reset(TextSelection());
|
_selection.reset(TextSelection());
|
||||||
|
|
||||||
|
auto was = base::take(_entries);
|
||||||
|
const auto wasViews = base::take(_views);
|
||||||
const auto item = quote.item;
|
const auto item = quote.item;
|
||||||
const auto group = item->history()->owner().groups().find(item);
|
const auto group = item->history()->owner().groups().find(item);
|
||||||
const auto leader = group ? group->items.front().get() : item;
|
const auto leader = group ? group->items.front().get() : item;
|
||||||
_element = leader->createView(_delegate.get());
|
_entries.push_back({
|
||||||
|
.view = leader->createView(_delegate.get()),
|
||||||
|
});
|
||||||
|
_views.emplace(_entries.back().view.get());
|
||||||
_link = _pressedLink = nullptr;
|
_link = _pressedLink = nullptr;
|
||||||
|
clear(std::move(was));
|
||||||
if (const auto was = base::take(_draftItem)) {
|
|
||||||
was->destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto media = item->media();
|
const auto media = item->media();
|
||||||
_onlyMessageText = media
|
_onlyMessageText = media
|
||||||
|
@ -249,12 +330,13 @@ rpl::producer<SelectedQuote> PreviewWrap::showQuoteSelector(
|
||||||
|| (!media->photo() && !media->document()));
|
|| (!media->photo() && !media->document()));
|
||||||
_section = Section::Reply;
|
_section = Section::Reply;
|
||||||
|
|
||||||
initElement();
|
initElements();
|
||||||
|
|
||||||
_selection = _element->selectionFromQuote(quote);
|
const auto view = _entries.back().view.get();
|
||||||
|
_selection = view->selectionFromQuote(quote);
|
||||||
return _selection.value(
|
return _selection.value(
|
||||||
) | rpl::map([=](TextSelection selection) {
|
) | rpl::map([=](TextSelection selection) {
|
||||||
if (const auto result = _element->selectedQuote(selection)) {
|
if (const auto result = view->selectedQuote(selection)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return SelectedQuote{ item };
|
return SelectedQuote{ item };
|
||||||
|
@ -267,13 +349,11 @@ rpl::producer<QString> PreviewWrap::showLinkSelector(
|
||||||
const std::vector<MessageLinkRange> &links,
|
const std::vector<MessageLinkRange> &links,
|
||||||
const QString &usedLink) {
|
const QString &usedLink) {
|
||||||
_selection.reset(TextSelection());
|
_selection.reset(TextSelection());
|
||||||
|
base::take(_views);
|
||||||
|
clear(base::take(_entries));
|
||||||
|
|
||||||
_element = nullptr;
|
|
||||||
if (const auto was = base::take(_draftItem)) {
|
|
||||||
was->destroy();
|
|
||||||
}
|
|
||||||
using Flag = MTPDmessageMediaWebPage::Flag;
|
using Flag = MTPDmessageMediaWebPage::Flag;
|
||||||
_draftItem = _history->addNewLocalMessage({
|
const auto item = _history->addNewLocalMessage({
|
||||||
.id = _history->nextNonHistoryEntryId(),
|
.id = _history->nextNonHistoryEntryId(),
|
||||||
.flags = (MessageFlag::FakeHistoryItem
|
.flags = (MessageFlag::FakeHistoryItem
|
||||||
| MessageFlag::Outgoing
|
| MessageFlag::Outgoing
|
||||||
|
@ -299,13 +379,15 @@ rpl::producer<QString> PreviewWrap::showLinkSelector(
|
||||||
MTP_long(webpage.id),
|
MTP_long(webpage.id),
|
||||||
MTP_string(webpage.url),
|
MTP_string(webpage.url),
|
||||||
MTP_int(0))));
|
MTP_int(0))));
|
||||||
_element = _draftItem->createView(_delegate.get());
|
_entries.push_back({ item, item->createView(_delegate.get()) });
|
||||||
|
_views.emplace(_entries.back().view.get());
|
||||||
|
|
||||||
_selectType = TextSelectType::Letters;
|
_selectType = TextSelectType::Letters;
|
||||||
_symbol = _selectionStartSymbol = 0;
|
_symbol = _selectionStartSymbol = 0;
|
||||||
_afterSymbol = _selectionStartAfterSymbol = false;
|
_afterSymbol = _selectionStartAfterSymbol = false;
|
||||||
_section = Section::Link;
|
_section = Section::Link;
|
||||||
|
|
||||||
initElement();
|
initElements();
|
||||||
highlightUsedLink(message, usedLink, links);
|
highlightUsedLink(message, usedLink, links);
|
||||||
|
|
||||||
return _chosenUrl.events();
|
return _chosenUrl.events();
|
||||||
|
@ -338,7 +420,8 @@ void PreviewWrap::highlightUsedLink(
|
||||||
text = text.mid(0, text.size() - 1);
|
text = text.mid(0, text.size() - 1);
|
||||||
--selection.to;
|
--selection.to;
|
||||||
}
|
}
|
||||||
const auto basic = _element->textState(QPoint(0, 0), {
|
const auto view = _entries.back().view.get();
|
||||||
|
const auto basic = view->textState(QPoint(0, 0), {
|
||||||
.flags = Ui::Text::StateRequest::Flag::LookupSymbol,
|
.flags = Ui::Text::StateRequest::Flag::LookupSymbol,
|
||||||
.onlyMessageText = true,
|
.onlyMessageText = true,
|
||||||
});
|
});
|
||||||
|
@ -353,30 +436,31 @@ void PreviewWrap::highlightUsedLink(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewWrap::paintEvent(QPaintEvent *e) {
|
void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
if (!_element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p = Painter(this);
|
auto p = Painter(this);
|
||||||
|
|
||||||
|
p.translate(_position);
|
||||||
|
|
||||||
auto context = _theme->preparePaintContext(
|
auto context = _theme->preparePaintContext(
|
||||||
_style.get(),
|
_style.get(),
|
||||||
rect(),
|
rect(),
|
||||||
e->rect(),
|
e->rect(),
|
||||||
!window()->isActiveWindow());
|
!window()->isActiveWindow());
|
||||||
context.outbg = _element->hasOutLayout();
|
for (const auto &entry : _entries) {
|
||||||
context.selection = _selecting
|
context.outbg = entry.view->hasOutLayout();
|
||||||
? resolveNewSelection()
|
context.selection = _selecting
|
||||||
: _selection.current();
|
? resolveNewSelection()
|
||||||
|
: _selection.current();
|
||||||
|
|
||||||
p.translate(_position);
|
entry.view->draw(p, context);
|
||||||
_element->draw(p, context);
|
|
||||||
|
|
||||||
if (_element->displayFromPhoto()) {
|
p.translate(0, entry.view->height());
|
||||||
|
}
|
||||||
|
const auto top = _entries.empty() ? nullptr : _entries.back().view.get();
|
||||||
|
if (top && top->displayFromPhoto()) {
|
||||||
auto userpicBottom = height()
|
auto userpicBottom = height()
|
||||||
- _element->marginBottom()
|
- top->marginBottom()
|
||||||
- _element->marginTop();
|
- top->marginTop();
|
||||||
const auto item = _element->data();
|
const auto item = top->data();
|
||||||
const auto userpicTop = userpicBottom - st::msgPhotoSize;
|
const auto userpicTop = userpicBottom - st::msgPhotoSize;
|
||||||
if (const auto from = item->displayFrom()) {
|
if (const auto from = item->displayFrom()) {
|
||||||
from->paintUserpicLeft(
|
from->paintUserpicLeft(
|
||||||
|
@ -415,7 +499,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewWrap::leaveEventHook(QEvent *e) {
|
void PreviewWrap::leaveEventHook(QEvent *e) {
|
||||||
if (!_element || !_over) {
|
if (!_over) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_over = false;
|
_over = false;
|
||||||
|
@ -427,7 +511,7 @@ void PreviewWrap::leaveEventHook(QEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewWrap::mouseMoveEvent(QMouseEvent *e) {
|
void PreviewWrap::mouseMoveEvent(QMouseEvent *e) {
|
||||||
if (!_element) {
|
if (_entries.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
using Flag = Ui::Text::StateRequest::Flag;
|
using Flag = Ui::Text::StateRequest::Flag;
|
||||||
|
@ -438,7 +522,16 @@ void PreviewWrap::mouseMoveEvent(QMouseEvent *e) {
|
||||||
.onlyMessageText = (_section == Section::Link || _onlyMessageText),
|
.onlyMessageText = (_section == Section::Link || _onlyMessageText),
|
||||||
};
|
};
|
||||||
const auto position = e->pos();
|
const auto position = e->pos();
|
||||||
auto resolved = _element->textState(position - _position, request);
|
auto local = position - _position;
|
||||||
|
auto resolved = TextState();
|
||||||
|
for (auto &entry : _entries) {
|
||||||
|
const auto height = entry.view->height();
|
||||||
|
if (local.y() < height) {
|
||||||
|
resolved = entry.view->textState(local, request);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
local.setY(local.y() - height);
|
||||||
|
}
|
||||||
_over = true;
|
_over = true;
|
||||||
const auto text = (_section == Section::Reply)
|
const auto text = (_section == Section::Reply)
|
||||||
&& (resolved.cursor == CursorState::Text);
|
&& (resolved.cursor == CursorState::Text);
|
||||||
|
@ -518,27 +611,25 @@ void PreviewWrap::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewWrap::initElement() {
|
void PreviewWrap::initElements() {
|
||||||
_elementLifetime.destroy();
|
for (auto &entry : _entries) {
|
||||||
|
entry.view->initDimensions();
|
||||||
if (!_element) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
_element->initDimensions();
|
|
||||||
|
|
||||||
widthValue(
|
widthValue(
|
||||||
) | rpl::filter([=](int width) {
|
) | rpl::filter([=](int width) {
|
||||||
return width > st::msgMinWidth;
|
return width > st::msgMinWidth;
|
||||||
}) | rpl::start_with_next([=](int width) {
|
}) | rpl::start_with_next([=](int width) {
|
||||||
const auto height = _position.y()
|
auto height = _position.y();
|
||||||
+ _element->resizeGetHeight(width)
|
for (const auto &entry : _entries) {
|
||||||
+ st::msgMargin.top();
|
height += entry.view->resizeGetHeight(width);
|
||||||
|
}
|
||||||
|
height += st::msgMargin.top();
|
||||||
resize(width, height);
|
resize(width, height);
|
||||||
}, _elementLifetime);
|
}, _elementLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSelection PreviewWrap::resolveNewSelection() const {
|
TextSelection PreviewWrap::resolveNewSelection() const {
|
||||||
if (_section != Section::Reply) {
|
if (_section != Section::Reply || _entries.empty()) {
|
||||||
return TextSelection();
|
return TextSelection();
|
||||||
}
|
}
|
||||||
const auto make = [](uint16 symbol, bool afterSymbol) {
|
const auto make = [](uint16 symbol, bool afterSymbol) {
|
||||||
|
@ -551,7 +642,7 @@ TextSelection PreviewWrap::resolveNewSelection() const {
|
||||||
const auto result = (first <= second)
|
const auto result = (first <= second)
|
||||||
? TextSelection{ first, second }
|
? TextSelection{ first, second }
|
||||||
: TextSelection{ second, first };
|
: TextSelection{ second, first };
|
||||||
return _element->adjustSelection(result, _selectType);
|
return _entries.back().view->adjustSelection(result, _selectType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewWrap::startSelection(TextSelectType type) {
|
void PreviewWrap::startSelection(TextSelectType type) {
|
||||||
|
@ -607,9 +698,10 @@ void DraftOptionsBox(
|
||||||
|
|
||||||
const auto &draft = args.draft;
|
const auto &draft = args.draft;
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<Section> shown;
|
rpl::variable<Section> shown = Section::Link;
|
||||||
rpl::lifetime shownLifetime;
|
rpl::lifetime shownLifetime;
|
||||||
rpl::variable<SelectedQuote> quote;
|
rpl::variable<SelectedQuote> quote;
|
||||||
|
Data::ResolvedForwardDraft forward;
|
||||||
Data::WebPageDraft webpage;
|
Data::WebPageDraft webpage;
|
||||||
WebPageData *preview = nullptr;
|
WebPageData *preview = nullptr;
|
||||||
QString link;
|
QString link;
|
||||||
|
@ -619,41 +711,90 @@ void DraftOptionsBox(
|
||||||
Fn<void(const QString &link, WebPageData *page)> performSwitch;
|
Fn<void(const QString &link, WebPageData *page)> performSwitch;
|
||||||
Fn<void(const QString &link, bool force)> requestAndSwitch;
|
Fn<void(const QString &link, bool force)> requestAndSwitch;
|
||||||
rpl::lifetime resolveLifetime;
|
rpl::lifetime resolveLifetime;
|
||||||
|
|
||||||
|
Fn<void()> rebuild;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
|
state->link = args.usedLink;
|
||||||
state->quote = SelectedQuote{
|
state->quote = SelectedQuote{
|
||||||
replyItem,
|
replyItem,
|
||||||
draft.reply.quote,
|
draft.reply.quote,
|
||||||
draft.reply.quoteOffset,
|
draft.reply.quoteOffset,
|
||||||
};
|
};
|
||||||
|
state->forward = std::move(args.forward);
|
||||||
state->webpage = draft.webpage;
|
state->webpage = draft.webpage;
|
||||||
state->preview = previewData;
|
state->preview = previewData;
|
||||||
state->shown = previewData ? Section::Link : Section::Reply;
|
|
||||||
if (replyItem && previewData) {
|
state->rebuild = [=] {
|
||||||
box->setNoContentMargin(true);
|
const auto hasLink = (state->preview != nullptr);
|
||||||
state->tabs = box->setPinnedToTopContent(
|
const auto hasReply = (state->quote.current().item != nullptr);
|
||||||
object_ptr<Ui::SettingsSlider>(
|
const auto hasForward = !state->forward.items.empty();
|
||||||
box.get(),
|
if (!hasLink && !hasReply && !hasForward) {
|
||||||
st::defaultTabsSlider));
|
box->closeBox();
|
||||||
state->tabs->resizeToWidth(st::boxWideWidth);
|
return;
|
||||||
state->tabs->move(0, 0);
|
}
|
||||||
state->tabs->setRippleTopRoundRadius(st::boxRadius);
|
const auto section = state->shown.current();
|
||||||
state->tabs->setSections({
|
const auto changeSection = (section == Section::Link)
|
||||||
tr::lng_reply_header_short(tr::now),
|
? !hasLink
|
||||||
tr::lng_link_header_short(tr::now),
|
: (section == Section::Reply)
|
||||||
});
|
? !hasReply
|
||||||
state->tabs->setActiveSectionFast(1);
|
: !hasForward;
|
||||||
state->tabs->sectionActivated(
|
const auto now = !changeSection
|
||||||
) | rpl::start_with_next([=](int section) {
|
? section
|
||||||
state->shown = section ? Section::Link : Section::Reply;
|
: hasLink
|
||||||
}, box->lifetime());
|
? Section::Link
|
||||||
} else {
|
: hasReply
|
||||||
box->setTitle(previewData
|
? Section::Reply
|
||||||
? tr::lng_link_options_header()
|
: Section::Forward;
|
||||||
: draft.reply.quote.empty()
|
auto labels = std::vector<QString>();
|
||||||
? tr::lng_reply_options_header()
|
auto indices = base::flat_map<Section, int>();
|
||||||
: tr::lng_reply_options_quote());
|
auto sections = std::vector<Section>();
|
||||||
}
|
const auto push = [&](Section section, tr::phrase<> phrase) {
|
||||||
|
indices[section] = labels.size();
|
||||||
|
labels.push_back(phrase(tr::now));
|
||||||
|
sections.push_back(section);
|
||||||
|
};
|
||||||
|
if (hasLink) {
|
||||||
|
push(Section::Link, tr::lng_link_header_short);
|
||||||
|
}
|
||||||
|
if (hasReply) {
|
||||||
|
push(Section::Reply, tr::lng_reply_header_short);
|
||||||
|
}
|
||||||
|
if (hasForward) {
|
||||||
|
push(Section::Forward, tr::lng_forward_header_short);
|
||||||
|
}
|
||||||
|
if (labels.size() > 1) {
|
||||||
|
box->setNoContentMargin(true);
|
||||||
|
state->tabs = box->setPinnedToTopContent(
|
||||||
|
object_ptr<Ui::SettingsSlider>(
|
||||||
|
box.get(),
|
||||||
|
st::defaultTabsSlider));
|
||||||
|
state->tabs->resizeToWidth(st::boxWideWidth);
|
||||||
|
state->tabs->move(0, 0);
|
||||||
|
state->tabs->setRippleTopRoundRadius(st::boxRadius);
|
||||||
|
state->tabs->setSections(labels);
|
||||||
|
state->tabs->setActiveSectionFast(indices[now]);
|
||||||
|
state->tabs->sectionActivated(
|
||||||
|
) | rpl::start_with_next([=](int index) {
|
||||||
|
state->shown = sections[index];
|
||||||
|
}, box->lifetime());
|
||||||
|
} else {
|
||||||
|
const auto forwardCount = state->forward.items.size();
|
||||||
|
box->setTitle(hasLink
|
||||||
|
? tr::lng_link_options_header()
|
||||||
|
: hasReply
|
||||||
|
? (state->quote.current().text.empty()
|
||||||
|
? tr::lng_reply_options_header()
|
||||||
|
: tr::lng_reply_options_quote())
|
||||||
|
: (forwardCount == 1)
|
||||||
|
? tr::lng_forward_title()
|
||||||
|
: tr::lng_forward_many_title(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(forwardCount * 1.0)));
|
||||||
|
}
|
||||||
|
state->shown.force_assign(now);
|
||||||
|
};
|
||||||
|
state->rebuild();
|
||||||
|
|
||||||
const auto bottom = box->setPinnedToBottomContent(
|
const auto bottom = box->setPinnedToBottomContent(
|
||||||
object_ptr<Ui::VerticalLayout>(box));
|
object_ptr<Ui::VerticalLayout>(box));
|
||||||
|
@ -675,9 +816,17 @@ void DraftOptionsBox(
|
||||||
};
|
};
|
||||||
const auto finish = [=](
|
const auto finish = [=](
|
||||||
FullReplyTo result,
|
FullReplyTo result,
|
||||||
Data::WebPageDraft webpage) {
|
Data::WebPageDraft webpage,
|
||||||
|
std::optional<Data::ForwardOptions> options) {
|
||||||
const auto weak = Ui::MakeWeak(box);
|
const auto weak = Ui::MakeWeak(box);
|
||||||
done(std::move(result), std::move(webpage));
|
auto forward = Data::ForwardDraft();
|
||||||
|
if (options) {
|
||||||
|
forward.options = *options;
|
||||||
|
for (const auto &item : state->forward.items) {
|
||||||
|
forward.ids.push_back(item->fullId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done(std::move(result), std::move(webpage), std::move(forward));
|
||||||
if (const auto strong = weak.data()) {
|
if (const auto strong = weak.data()) {
|
||||||
strong->closeBox();
|
strong->closeBox();
|
||||||
}
|
}
|
||||||
|
@ -716,7 +865,7 @@ void DraftOptionsBox(
|
||||||
st::settingsAttentionButtonWithIcon,
|
st::settingsAttentionButtonWithIcon,
|
||||||
{ &st::menuIconDeleteAttention }
|
{ &st::menuIconDeleteAttention }
|
||||||
)->setClickedCallback([=] {
|
)->setClickedCallback([=] {
|
||||||
finish({}, state->webpage);
|
finish({}, state->webpage, state->forward.options);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!item->originalText().empty()) {
|
if (!item->originalText().empty()) {
|
||||||
|
@ -774,7 +923,8 @@ void DraftOptionsBox(
|
||||||
st::settingsAttentionButtonWithIcon,
|
st::settingsAttentionButtonWithIcon,
|
||||||
{ &st::menuIconDeleteAttention }
|
{ &st::menuIconDeleteAttention }
|
||||||
)->setClickedCallback([=] {
|
)->setClickedCallback([=] {
|
||||||
finish(resolveReply(), { .removed = true });
|
const auto options = state->forward.options;
|
||||||
|
finish(resolveReply(), { .removed = true }, options);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (args.links.size() > 1) {
|
if (args.links.size() > 1) {
|
||||||
|
@ -783,6 +933,92 @@ void DraftOptionsBox(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto setupForwardActions = [=] {
|
||||||
|
using Options = Data::ForwardOptions;
|
||||||
|
const auto now = state->forward.options;
|
||||||
|
const auto &items = state->forward.items;
|
||||||
|
const auto count = items.size();
|
||||||
|
const auto dropNames = (now != Options::PreserveInfo);
|
||||||
|
const auto sendersCount = ItemsForwardSendersCount(items);
|
||||||
|
const auto captionsCount = ItemsForwardCaptionsCount(items);
|
||||||
|
const auto hasOnlyForcedForwardedInfo = !captionsCount
|
||||||
|
&& HasOnlyForcedForwardedInfo(items);
|
||||||
|
const auto dropCaptions = (now == Options::NoNamesAndCaptions);
|
||||||
|
|
||||||
|
AddFilledSkip(bottom);
|
||||||
|
|
||||||
|
if (!hasOnlyForcedForwardedInfo) {
|
||||||
|
Settings::AddButtonWithIcon(
|
||||||
|
bottom,
|
||||||
|
(dropNames
|
||||||
|
? (sendersCount == 1
|
||||||
|
? tr::lng_forward_action_show_sender
|
||||||
|
: tr::lng_forward_action_show_senders)
|
||||||
|
: (sendersCount == 1
|
||||||
|
? tr::lng_forward_action_hide_sender
|
||||||
|
: tr::lng_forward_action_hide_senders))(),
|
||||||
|
st::settingsButton,
|
||||||
|
{ dropNames
|
||||||
|
? &st::menuIconUserShow
|
||||||
|
: &st::menuIconUserHide }
|
||||||
|
)->setClickedCallback([=] {
|
||||||
|
state->forward.options = dropNames
|
||||||
|
? Options::PreserveInfo
|
||||||
|
: Options::NoSenderNames;
|
||||||
|
state->shown.force_assign(Section::Forward);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (captionsCount) {
|
||||||
|
Settings::AddButtonWithIcon(
|
||||||
|
bottom,
|
||||||
|
(dropCaptions
|
||||||
|
? (captionsCount == 1
|
||||||
|
? tr::lng_forward_action_show_caption
|
||||||
|
: tr::lng_forward_action_show_captions)
|
||||||
|
: (captionsCount == 1
|
||||||
|
? tr::lng_forward_action_hide_caption
|
||||||
|
: tr::lng_forward_action_hide_captions))(),
|
||||||
|
st::settingsButton,
|
||||||
|
{ dropCaptions
|
||||||
|
? &st::menuIconCaptionShow
|
||||||
|
: &st::menuIconCaptionHide }
|
||||||
|
)->setClickedCallback([=] {
|
||||||
|
state->forward.options = dropCaptions
|
||||||
|
? Options::NoSenderNames
|
||||||
|
: Options::NoNamesAndCaptions;
|
||||||
|
state->shown.force_assign(Section::Forward);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::AddButtonWithIcon(
|
||||||
|
bottom,
|
||||||
|
tr::lng_forward_action_change_recipient(),
|
||||||
|
st::settingsButton,
|
||||||
|
{ &st::menuIconReplace }
|
||||||
|
)->setClickedCallback([=] {
|
||||||
|
auto draft = base::take(state->forward);
|
||||||
|
finish(resolveReply(), state->webpage, std::nullopt);
|
||||||
|
Window::ShowForwardMessagesBox(show, {
|
||||||
|
.ids = show->session().data().itemsToIds(draft.items),
|
||||||
|
.options = draft.options,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Settings::AddButtonWithIcon(
|
||||||
|
bottom,
|
||||||
|
tr::lng_forward_action_remove(),
|
||||||
|
st::settingsAttentionButtonWithIcon,
|
||||||
|
{ &st::menuIconDeleteAttention }
|
||||||
|
)->setClickedCallback([=] {
|
||||||
|
finish(resolveReply(), state->webpage, std::nullopt);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddFilledSkip(bottom);
|
||||||
|
Ui::AddDividerText(bottom, (count == 1
|
||||||
|
? tr::lng_forward_about()
|
||||||
|
: tr::lng_forward_many_about()));
|
||||||
|
};
|
||||||
|
|
||||||
const auto &resolver = args.resolver;
|
const auto &resolver = args.resolver;
|
||||||
state->performSwitch = [=](const QString &link, WebPageData *page) {
|
state->performSwitch = [=](const QString &link, WebPageData *page) {
|
||||||
const auto now = base::unixtime::now();
|
const auto now = base::unixtime::now();
|
||||||
|
@ -847,20 +1083,27 @@ void DraftOptionsBox(
|
||||||
state->shown.value() | rpl::start_with_next([=](Section shown) {
|
state->shown.value() | rpl::start_with_next([=](Section shown) {
|
||||||
bottom->clear();
|
bottom->clear();
|
||||||
state->shownLifetime.destroy();
|
state->shownLifetime.destroy();
|
||||||
if (shown == Section::Reply) {
|
switch (shown) {
|
||||||
state->quote = state->wrap->showQuoteSelector(
|
case Section::Reply: {
|
||||||
state->quote.current());
|
state->quote = state->wrap->showQuoteSelector(
|
||||||
setupReplyActions();
|
state->quote.current());
|
||||||
} else {
|
setupReplyActions();
|
||||||
state->wrap->showLinkSelector(
|
} break;
|
||||||
draft.textWithTags,
|
case Section::Link: {
|
||||||
state->webpage,
|
state->wrap->showLinkSelector(
|
||||||
linkRanges,
|
draft.textWithTags,
|
||||||
state->link
|
state->webpage,
|
||||||
) | rpl::start_with_next([=](QString link) {
|
linkRanges,
|
||||||
switchTo(link);
|
state->link
|
||||||
}, state->shownLifetime);
|
) | rpl::start_with_next([=](QString link) {
|
||||||
setupLinkActions();
|
switchTo(link);
|
||||||
|
}, state->shownLifetime);
|
||||||
|
setupLinkActions();
|
||||||
|
} break;
|
||||||
|
case Section::Forward: {
|
||||||
|
state->wrap->showForwardSelector(state->forward);
|
||||||
|
setupForwardActions();
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
|
|
||||||
|
@ -879,7 +1122,8 @@ void DraftOptionsBox(
|
||||||
.text = { tr::lng_reply_quote_long_text(tr::now) },
|
.text = { tr::lng_reply_quote_long_text(tr::now) },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
finish(resolveReply(), state->webpage);
|
const auto options = state->forward.options;
|
||||||
|
finish(resolveReply(), state->webpage, options);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -887,31 +1131,29 @@ void DraftOptionsBox(
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (replyItem) {
|
args.show->session().data().itemRemoved(
|
||||||
args.show->session().data().itemRemoved(
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> removed) {
|
||||||
) | rpl::filter([=](not_null<const HistoryItem*> removed) {
|
const auto inReply = (state->quote.current().item == removed);
|
||||||
const auto current = state->quote.current().item;
|
if (inReply) {
|
||||||
if ((removed == replyItem) || (removed == current)) {
|
state->quote = SelectedQuote();
|
||||||
return true;
|
}
|
||||||
}
|
const auto i = ranges::find(state->forward.items, removed);
|
||||||
const auto group = current->history()->owner().groups().find(
|
const auto inForward = (i != end(state->forward.items));
|
||||||
current);
|
if (inForward) {
|
||||||
return (group && ranges::contains(group->items, removed));
|
state->forward.items.erase(i);
|
||||||
}) | rpl::start_with_next([=] {
|
}
|
||||||
if (previewData) {
|
if (inReply || inForward) {
|
||||||
state->tabs = nullptr;
|
state->rebuild();
|
||||||
box->setPinnedToTopContent(
|
}
|
||||||
object_ptr<Ui::RpWidget>(nullptr));
|
}, box->lifetime());
|
||||||
box->setNoContentMargin(false);
|
|
||||||
box->setTitle(state->quote.current().text.empty()
|
args.show->session().data().itemViewRefreshRequest(
|
||||||
? tr::lng_reply_options_header()
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||||
: tr::lng_reply_options_quote());
|
if (state->wrap->hasViewForItem(item)) {
|
||||||
state->shown = Section::Link;
|
state->rebuild();
|
||||||
} else {
|
}
|
||||||
box->closeBox();
|
}, box->lifetime());
|
||||||
}
|
|
||||||
}, box->lifetime());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AuthorSelector {
|
struct AuthorSelector {
|
||||||
|
@ -1165,7 +1407,7 @@ void EditDraftOptions(EditDraftOptionsArgs &&args) {
|
||||||
&& !previewDataRaw->failed)
|
&& !previewDataRaw->failed)
|
||||||
? previewDataRaw
|
? previewDataRaw
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (!replyItem && !previewData) {
|
if (!replyItem && !previewData && args.forward.items.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
args.show->show(
|
args.show->show(
|
||||||
|
|
|
@ -29,9 +29,10 @@ struct EditDraftOptionsArgs {
|
||||||
not_null<History*> history;
|
not_null<History*> history;
|
||||||
Data::Draft draft;
|
Data::Draft draft;
|
||||||
QString usedLink;
|
QString usedLink;
|
||||||
|
Data::ResolvedForwardDraft forward;
|
||||||
std::vector<MessageLinkRange> links;
|
std::vector<MessageLinkRange> links;
|
||||||
std::shared_ptr<WebpageResolver> resolver;
|
std::shared_ptr<WebpageResolver> resolver;
|
||||||
Fn<void(FullReplyTo, Data::WebPageDraft)> done;
|
Fn<void(FullReplyTo, Data::WebPageDraft, Data::ForwardDraft)> done;
|
||||||
Fn<void(FullReplyTo)> highlight;
|
Fn<void(FullReplyTo)> highlight;
|
||||||
Fn<void()> clearOldDraft;
|
Fn<void()> clearOldDraft;
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,19 +44,6 @@ constexpr auto kUnknownVersion = -1;
|
||||||
constexpr auto kNameWithCaptionsVersion = -2;
|
constexpr auto kNameWithCaptionsVersion = -2;
|
||||||
constexpr auto kNameNoCaptionsVersion = -3;
|
constexpr auto kNameNoCaptionsVersion = -3;
|
||||||
|
|
||||||
[[nodiscard]] bool HasOnlyForcedForwardedInfo(const HistoryItemsList &list) {
|
|
||||||
for (const auto &item : list) {
|
|
||||||
if (const auto media = item->media()) {
|
|
||||||
if (!media->forceForwardedInfo()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ForwardPanel::ForwardPanel(Fn<void()> repaint)
|
ForwardPanel::ForwardPanel(Fn<void()> repaint)
|
||||||
|
@ -228,6 +215,10 @@ void ForwardPanel::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Data::ResolvedForwardDraft &ForwardPanel::draft() const {
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
const HistoryItemsList &ForwardPanel::items() const {
|
const HistoryItemsList &ForwardPanel::items() const {
|
||||||
return _data.items;
|
return _data.items;
|
||||||
}
|
}
|
||||||
|
@ -236,63 +227,17 @@ bool ForwardPanel::empty() const {
|
||||||
return _data.items.empty();
|
return _data.items.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardPanel::editOptions(std::shared_ptr<ChatHelpers::Show> show) {
|
void ForwardPanel::applyOptions(Data::ForwardOptions options) {
|
||||||
using Options = Data::ForwardOptions;
|
if (_data.items.empty()) {
|
||||||
const auto now = _data.options;
|
|
||||||
const auto count = _data.items.size();
|
|
||||||
const auto dropNames = (now != Options::PreserveInfo);
|
|
||||||
const auto sendersCount = ItemsForwardSendersCount(_data.items);
|
|
||||||
const auto captionsCount = ItemsForwardCaptionsCount(_data.items);
|
|
||||||
const auto hasOnlyForcedForwardedInfo = !captionsCount
|
|
||||||
&& HasOnlyForcedForwardedInfo(_data.items);
|
|
||||||
const auto dropCaptions = (now == Options::NoNamesAndCaptions);
|
|
||||||
const auto weak = base::make_weak(this);
|
|
||||||
const auto changeRecipient = crl::guard(this, [=] {
|
|
||||||
if (_data.items.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto data = base::take(_data);
|
|
||||||
_to->owningHistory()->setForwardDraft(_to->topicRootId(), {});
|
|
||||||
Window::ShowForwardMessagesBox(show, {
|
|
||||||
.ids = _to->owner().itemsToIds(data.items),
|
|
||||||
.options = data.options,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (hasOnlyForcedForwardedInfo) {
|
|
||||||
changeRecipient();
|
|
||||||
return;
|
return;
|
||||||
|
} else if (_data.options != options) {
|
||||||
|
_data.options = options;
|
||||||
|
_to->owningHistory()->setForwardDraft(_to->topicRootId(), {
|
||||||
|
.ids = _to->owner().itemsToIds(_data.items),
|
||||||
|
.options = options,
|
||||||
|
});
|
||||||
|
_repaint();
|
||||||
}
|
}
|
||||||
const auto optionsChanged = crl::guard(weak, [=](
|
|
||||||
Ui::ForwardOptions options) {
|
|
||||||
if (_data.items.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto newOptions = (options.captionsCount
|
|
||||||
&& options.dropCaptions)
|
|
||||||
? Options::NoNamesAndCaptions
|
|
||||||
: options.dropNames
|
|
||||||
? Options::NoSenderNames
|
|
||||||
: Options::PreserveInfo;
|
|
||||||
if (_data.options != newOptions) {
|
|
||||||
_data.options = newOptions;
|
|
||||||
_to->owningHistory()->setForwardDraft(_to->topicRootId(), {
|
|
||||||
.ids = _to->owner().itemsToIds(_data.items),
|
|
||||||
.options = newOptions,
|
|
||||||
});
|
|
||||||
_repaint();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
show->showBox(Box(
|
|
||||||
Ui::ForwardOptionsBox,
|
|
||||||
count,
|
|
||||||
Ui::ForwardOptions{
|
|
||||||
.sendersCount = sendersCount,
|
|
||||||
.captionsCount = captionsCount,
|
|
||||||
.dropNames = dropNames,
|
|
||||||
.dropCaptions = dropCaptions,
|
|
||||||
},
|
|
||||||
optionsChanged,
|
|
||||||
changeRecipient));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardPanel::editToNextOption() {
|
void ForwardPanel::editToNextOption() {
|
||||||
|
@ -485,7 +430,19 @@ void EditWebPageOptions(
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasOnlyForcedForwardedInfo(const HistoryItemsList &list) {
|
||||||
|
for (const auto &item : list) {
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (!media->forceForwardedInfo()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace HistoryView::Controls
|
} // namespace HistoryView::Controls
|
||||||
|
|
|
@ -47,9 +47,10 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> itemsUpdated() const;
|
[[nodiscard]] rpl::producer<> itemsUpdated() const;
|
||||||
|
|
||||||
void editOptions(std::shared_ptr<ChatHelpers::Show> show);
|
void applyOptions(Data::ForwardOptions options);
|
||||||
void editToNextOption();
|
void editToNextOption();
|
||||||
|
|
||||||
|
[[nodiscard]] const Data::ResolvedForwardDraft &draft() const;
|
||||||
[[nodiscard]] const HistoryItemsList &items() const;
|
[[nodiscard]] const HistoryItemsList &items() const;
|
||||||
[[nodiscard]] bool empty() const;
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
||||||
|
@ -83,4 +84,6 @@ void EditWebPageOptions(
|
||||||
Data::WebPageDraft draft,
|
Data::WebPageDraft draft,
|
||||||
Fn<void(Data::WebPageDraft)> done);
|
Fn<void(Data::WebPageDraft)> done);
|
||||||
|
|
||||||
|
[[nodiscard]] bool HasOnlyForcedForwardedInfo(const HistoryItemsList &list);
|
||||||
|
|
||||||
} // namespace HistoryView::Controls
|
} // namespace HistoryView::Controls
|
||||||
|
|
|
@ -65,72 +65,4 @@ void FillForwardOptions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardOptionsBox(
|
|
||||||
not_null<GenericBox*> box,
|
|
||||||
int count,
|
|
||||||
ForwardOptions options,
|
|
||||||
Fn<void(ForwardOptions)> optionsChanged,
|
|
||||||
Fn<void()> changeRecipient) {
|
|
||||||
Expects(optionsChanged != nullptr);
|
|
||||||
Expects(changeRecipient != nullptr);
|
|
||||||
|
|
||||||
box->setTitle((count == 1)
|
|
||||||
? tr::lng_forward_title()
|
|
||||||
: tr::lng_forward_many_title(
|
|
||||||
lt_count,
|
|
||||||
rpl::single(count) | tr::to_count()));
|
|
||||||
box->addButton(tr::lng_box_done(), [=] {
|
|
||||||
box->closeBox();
|
|
||||||
});
|
|
||||||
|
|
||||||
box->events(
|
|
||||||
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
|
||||||
if (e->type() == QEvent::KeyPress) {
|
|
||||||
const auto k = static_cast<QKeyEvent*>(e.get());
|
|
||||||
if (k->key() == Qt::Key_Enter || k->key() == Qt::Key_Return) {
|
|
||||||
box->closeBox();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, box->lifetime());
|
|
||||||
|
|
||||||
box->addRow(
|
|
||||||
object_ptr<Ui::FlatLabel>(
|
|
||||||
box.get(),
|
|
||||||
(count == 1
|
|
||||||
? tr::lng_forward_about()
|
|
||||||
: tr::lng_forward_many_about()),
|
|
||||||
st::boxLabel),
|
|
||||||
st::boxRowPadding);
|
|
||||||
const auto checkboxPadding = style::margins(
|
|
||||||
st::boxRowPadding.left(),
|
|
||||||
st::boxRowPadding.left(),
|
|
||||||
st::boxRowPadding.right(),
|
|
||||||
st::boxRowPadding.bottom());
|
|
||||||
|
|
||||||
auto createView = [&](rpl::producer<QString> &&text, bool checked) {
|
|
||||||
return box->addRow(
|
|
||||||
object_ptr<Ui::Checkbox>(
|
|
||||||
box.get(),
|
|
||||||
std::move(text),
|
|
||||||
checked,
|
|
||||||
st::defaultBoxCheckbox),
|
|
||||||
checkboxPadding)->checkView();
|
|
||||||
};
|
|
||||||
FillForwardOptions(
|
|
||||||
std::move(createView),
|
|
||||||
options,
|
|
||||||
std::move(optionsChanged),
|
|
||||||
box->lifetime());
|
|
||||||
|
|
||||||
box->addRow(
|
|
||||||
object_ptr<Ui::LinkButton>(
|
|
||||||
box.get(),
|
|
||||||
tr::lng_forward_change_recipient(tr::now)),
|
|
||||||
checkboxPadding
|
|
||||||
)->setClickedCallback([=] {
|
|
||||||
box->closeBox();
|
|
||||||
changeRecipient();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -28,11 +28,4 @@ void FillForwardOptions(
|
||||||
Fn<void(ForwardOptions)> optionsChanged,
|
Fn<void(ForwardOptions)> optionsChanged,
|
||||||
rpl::lifetime &lifetime);
|
rpl::lifetime &lifetime);
|
||||||
|
|
||||||
void ForwardOptionsBox(
|
|
||||||
not_null<GenericBox*> box,
|
|
||||||
int count,
|
|
||||||
ForwardOptions options,
|
|
||||||
Fn<void(ForwardOptions)> optionsChanged,
|
|
||||||
Fn<void()> changeRecipient);
|
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -150,6 +150,10 @@ menuIconAbove: icon {{ "menu/link_above", menuIconColor }};
|
||||||
menuIconBelow: icon {{ "menu/link_below", menuIconColor }};
|
menuIconBelow: icon {{ "menu/link_below", menuIconColor }};
|
||||||
menuIconEnlarge: icon {{ "menu/link_enlarge", menuIconColor }};
|
menuIconEnlarge: icon {{ "menu/link_enlarge", menuIconColor }};
|
||||||
menuIconShrink: icon {{ "menu/link_shrink", menuIconColor }};
|
menuIconShrink: icon {{ "menu/link_shrink", menuIconColor }};
|
||||||
|
menuIconUserShow: icon {{ "menu/name_show", menuIconColor }};
|
||||||
|
menuIconUserHide: icon {{ "menu/name_hide", menuIconColor }};
|
||||||
|
menuIconCaptionShow: icon {{ "menu/caption_show", menuIconColor }};
|
||||||
|
menuIconCaptionHide: icon {{ "menu/caption_hide", menuIconColor }};
|
||||||
menuIconAsTopics: icon {{ "menu/mode_topics", menuIconColor }};
|
menuIconAsTopics: icon {{ "menu/mode_topics", menuIconColor }};
|
||||||
menuIconAsMessages: icon {{ "menu/mode_messages", menuIconColor }};
|
menuIconAsMessages: icon {{ "menu/mode_messages", menuIconColor }};
|
||||||
menuIconTagFilter: icon{{ "menu/tag_filter", menuIconColor }};
|
menuIconTagFilter: icon{{ "menu/tag_filter", menuIconColor }};
|
||||||
|
|