Add a fast share button in channels and bots.

This commit is contained in:
John Preston 2017-07-11 20:11:06 +03:00
parent ac99784bf7
commit f32af6999b
13 changed files with 443 additions and 237 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

View file

@ -34,6 +34,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/toast/toast.h"
#include "ui/widgets/multi_select.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "window/themes/window_theme.h"
@ -208,7 +209,7 @@ void ShareBox::createButtons() {
clearButtons();
if (_hasSelected) {
addButton(langFactory(lng_share_confirm), [this] { onSubmit(); });
} else {
} else if (_copyCallback) {
addButton(langFactory(lng_share_copy_link), [this] { onCopyLink(); });
}
addButton(langFactory(lng_cancel), [this] { closeBox(); });
@ -840,103 +841,6 @@ QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
return url + shareComponent;
}
namespace {
void ShareGameScoreFromItem(HistoryItem *item) {
struct ShareGameScoreData {
ShareGameScoreData(const FullMsgId &msgId) : msgId(msgId) {
}
FullMsgId msgId;
OrderedSet<mtpRequestId> requests;
};
auto data = MakeShared<ShareGameScoreData>(item->fullId());
auto copyCallback = [data]() {
if (auto main = App::main()) {
if (auto item = App::histItemById(data->msgId)) {
if (auto bot = item->getMessageBot()) {
if (auto media = item->getMedia()) {
if (media->type() == MediaTypeGame) {
auto shortName = static_cast<HistoryGame*>(media)->game()->shortName;
QApplication::clipboard()->setText(Messenger::Instance().createInternalLinkFull(bot->username + qsl("?game=") + shortName));
Ui::Toast::Show(lang(lng_share_game_link_copied));
}
}
}
}
}
};
auto submitCallback = [data](const QVector<PeerData*> &result) {
if (!data->requests.empty()) {
return; // Share clicked already.
}
if (result.empty()) {
return;
}
auto restrictedEverywhere = true;
auto restrictedSomewhere = false;
for_const (auto peer, result) {
if (auto megagroup = peer->asMegagroup()) {
if (megagroup->restrictedRights().is_send_games()) {
restrictedSomewhere = true;
continue;
}
}
restrictedEverywhere = false;
}
if (restrictedEverywhere) {
Ui::show(Box<InformBox>(lang(lng_restricted_send_inline)), KeepOtherLayers);
return;
}
auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) {
if (auto main = App::main()) {
main->sentUpdatesReceived(updates);
}
data->requests.remove(requestId);
if (data->requests.empty()) {
Ui::Toast::Show(lang(lng_share_done));
Ui::hideLayer();
}
};
auto sendFlags = MTPmessages_ForwardMessages::Flag::f_with_my_score;
MTPVector<MTPint> msgIds = MTP_vector<MTPint>(1, MTP_int(data->msgId.msg));
if (auto main = App::main()) {
if (auto item = App::histItemById(data->msgId)) {
for_const (auto peer, result) {
if (auto megagroup = peer->asMegagroup()) {
if (megagroup->restrictedRights().is_send_games()) {
continue;
}
}
MTPVector<MTPlong> random = MTP_vector<MTPlong>(1, rand_value<MTPlong>());
auto request = MTPmessages_ForwardMessages(MTP_flags(sendFlags), item->history()->peer->input, msgIds, random, peer->input);
auto callback = doneCallback;
auto requestId = MTP::send(request, rpcDone(std::move(callback)));
data->requests.insert(requestId);
}
}
}
};
auto filterCallback = [](PeerData *peer) {
if (peer->canWrite()) {
if (auto channel = peer->asChannel()) {
return !channel->isBroadcast();
}
return true;
}
return false;
};
Ui::show(Box<ShareBox>(std::move(copyCallback), std::move(submitCallback), std::move(filterCallback)));
}
} // namespace
void ShareGameScoreByHash(const QString &hash) {
auto key128Size = 0x10;
@ -988,12 +892,12 @@ void ShareGameScoreByHash(const QString &hash) {
}
if (auto item = App::histItemById(channelId, msgId)) {
ShareGameScoreFromItem(item);
FastShareMessage(item);
} else if (App::api()) {
auto resolveMessageAndShareScore = [msgId](ChannelData *channel) {
App::api()->requestMessageData(channel, msgId, [](ChannelData *channel, MsgId msgId) {
if (auto item = App::histItemById(channel, msgId)) {
ShareGameScoreFromItem(item);
FastShareMessage(item);
} else {
Ui::show(Box<InformBox>(lang(lng_edit_deleted)));
}

View file

@ -483,3 +483,8 @@ historyAdminLogCancelSearch: CrossButton {
}
historyAdminLogSearchTop: 11px;
historyAdminLogSearchSlideDuration: 150;
historyFastShareSize: 31px;
historyFastShareLeft: 13px;
historyFastShareBottom: 5px;
historyFastShareIcon: icon {{ "fast_share", msgServiceFg, point(4px, 3px)}};

View file

@ -692,6 +692,14 @@ public:
virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
}
virtual ClickHandlerPtr fastShareLink() const {
return ClickHandlerPtr();
}
virtual bool displayFastShare() const {
return false;
}
virtual void drawFastShare(Painter &p, int left, int top, int outerWidth) const {
}
virtual void setViewsCount(int32 count) {
}
virtual void setId(MsgId newId);

View file

@ -59,6 +59,9 @@ public:
virtual bool hasTextForCopy() const {
return false;
}
virtual bool allowsFastShare() const {
return false;
}
virtual void initDimensions() = 0;
virtual void updateMessageId() {
}

View file

@ -453,14 +453,20 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim
}
// date
if (_caption.isEmpty()) {
if (notChild && (_data->uploading() || App::hoveredItem() == _parent)) {
int32 fullRight = skipx + width, fullBottom = skipy + height;
_parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
}
} else {
if (!_caption.isEmpty()) {
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
_caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection);
} else if (notChild) {
auto fullRight = skipx + width;
auto fullBottom = skipy + height;
if (_data->uploading() || App::hoveredItem() == _parent) {
_parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
}
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
_parent->drawFastShare(p, fastShareLeft, fastShareTop, 2 * skipx + width);
}
}
}
@ -469,7 +475,7 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
int skipx = 0, skipy = 0, width = _width, height = _height;
bool bubble = _parent->hasBubble();
auto bubble = _parent->hasBubble();
if (bubble) {
skipx = st::mediaPadding.left();
@ -502,14 +508,20 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques
} else {
result.link = _savel;
}
if (_caption.isEmpty() && _parent->getMedia() == this) {
auto fullRight = skipx + width;
auto fullBottom = skipy + height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (_caption.isEmpty() && _parent->getMedia() == this) {
auto fullRight = skipx + width;
auto fullBottom = skipy + height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = _parent->fastShareLink();
}
}
return result;
}
return result;
}
@ -830,14 +842,17 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim
p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x());
// date
if (_caption.isEmpty()) {
if (_parent->getMedia() == this) {
int32 fullRight = skipx + width, fullBottom = skipy + height;
_parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
}
} else {
if (!_caption.isEmpty()) {
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
_caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection);
} else if (_parent->getMedia() == this) {
auto fullRight = skipx + width, fullBottom = skipy + height;
_parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
_parent->drawFastShare(p, fastShareLeft, fastShareTop, 2 * skipx + width);
}
}
}
@ -874,14 +889,20 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
} else {
result.link = loaded ? _openl : (_data->loading() ? _cancell : _savel);
}
if (_caption.isEmpty() && _parent->getMedia() == this) {
auto fullRight = skipx + width;
auto fullBottom = skipy + height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (_caption.isEmpty() && _parent->getMedia() == this) {
auto fullRight = skipx + width;
auto fullBottom = skipy + height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = _parent->fastShareLink();
}
}
return result;
}
return result;
}
@ -2160,30 +2181,40 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
}
}
}
if (!_caption.isEmpty()) {
if (!isRound && !_caption.isEmpty()) {
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
_caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection);
} else if (!isChildMedia && (isRound || _data->uploading() || App::hoveredItem() == _parent)) {
} else if (!isChildMedia) {
auto fullRight = skipx + usex + usew;
auto fullBottom = skipy + height;
auto maxRight = _parent->history()->width - st::msgMargin.left();
if (_parent->history()->canHaveFromPhotos()) {
maxRight -= st::msgMargin.right();
} else {
maxRight -= st::msgMargin.left();
}
if (isRound && !outbg) {
auto infoWidth = _parent->infoWidth();
// This is just some arbitrary point,
// the main idea is to make info left aligned here.
fullRight += infoWidth - st::normalFont->height;
auto maxRight = _parent->history()->width - st::msgMargin.left();
if (_parent->history()->canHaveFromPhotos()) {
maxRight -= st::msgMargin.right();
} else {
maxRight -= st::msgMargin.left();
}
if (fullRight > maxRight) {
fullRight = maxRight;
}
}
_parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, isRound ? InfoDisplayOverBackground : InfoDisplayOverImage);
if (isRound || _data->uploading() || App::hoveredItem() == _parent) {
_parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, isRound ? InfoDisplayOverBackground : InfoDisplayOverImage);
}
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (fastShareLeft + st::historyFastShareSize > maxRight) {
fastShareLeft = (fullRight - st::historyFastShareSize - st::msgDateImgDelta);
fastShareTop -= (st::msgDateImgDelta + st::msgDateImgPadding.y() + st::msgDateFont->height + st::msgDateImgPadding.y());
}
_parent->drawFastShare(p, fastShareLeft, fastShareTop, 2 * skipx + width);
}
}
}
@ -2212,8 +2243,9 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
width -= st::mediaPadding.left() + st::mediaPadding.right();
height -= skipy + st::mediaPadding.bottom();
}
auto out = _parent->out(), isPost = _parent->isPost();
bool out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost;
auto isChildMedia = (_parent->getMedia() != this);
auto isRound = _data->isRoundVideo();
auto usew = width, usex = 0;
auto separateRoundVideo = isSeparateRoundVideo();
auto via = separateRoundVideo ? _parent->Get<HistoryMessageVia>() : nullptr;
@ -2290,14 +2322,42 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
} else {
result.link = _openInMediaviewLink;
}
}
if (isRound || _caption.isEmpty()) {
auto fullRight = usex + skipx + usew;
auto fullBottom = skipy + height;
auto maxRight = _parent->history()->width - st::msgMargin.left();
if (_parent->history()->canHaveFromPhotos()) {
maxRight -= st::msgMargin.right();
} else {
maxRight -= st::msgMargin.left();
}
if (isRound && !outbg) {
auto infoWidth = _parent->infoWidth();
// This is just some arbitrary point,
// the main idea is to make info left aligned here.
fullRight += infoWidth - st::normalFont->height;
if (fullRight > maxRight) {
fullRight = maxRight;
}
}
if (!isChildMedia) {
auto fullRight = usex + skipx + usew;
auto fullBottom = skipy + height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
if (_parent->pointInTime(fullRight, fullBottom, point, isRound ? InfoDisplayOverBackground : InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
}
return result;
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (fastShareLeft + st::historyFastShareSize > maxRight) {
fastShareLeft = (fullRight - st::historyFastShareSize - st::msgDateImgDelta);
fastShareTop -= st::msgDateImgDelta + st::msgDateImgPadding.y() + st::msgDateFont->height + st::msgDateImgPadding.y();
}
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = _parent->fastShareLink();
}
}
}
return result;
}
@ -2637,8 +2697,9 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
}
if (!childmedia) {
_parent->drawInfo(p, usex + usew, _height, usex * 2 + usew, selected, InfoDisplayOverBackground);
auto fullRight = usex + usew;
auto fullBottom = _height;
_parent->drawInfo(p, fullRight, fullBottom, usex * 2 + usew, selected, InfoDisplayOverBackground);
if (via || reply) {
int rectw = _width - usew - st::msgReplyPadding.left();
int recth = st::msgReplyPadding.top() + st::msgReplyPadding.bottom();
@ -2673,6 +2734,11 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
reply->paint(p, _parent, rectx, recty, rectw, flags);
}
}
if (_parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
_parent->drawFastShare(p, fastShareLeft, fastShareTop, 2 * usex + usew);
}
}
}
@ -2728,9 +2794,18 @@ HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest requ
}
}
if (_parent->getMedia() == this) {
if (_parent->pointInTime(usex + usew, _height, point, InfoDisplayOverImage)) {
auto fullRight = usex + usew;
auto fullBottom = _height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (_parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = _parent->fastShareLink();
}
}
}
auto pixLeft = usex + (usew - _pixw) / 2;
@ -4614,8 +4689,14 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
}
if (_parent->getMedia() == this) {
int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
auto fullRight = skipx + width;
auto fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
_parent->drawInfo(p, fullRight, fullBottom, skipx * 2 + width, selected, InfoDisplayOverImage);
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
_parent->drawFastShare(p, fastShareLeft, fastShareTop, 2 * skipx + width);
}
}
}
@ -4666,12 +4747,20 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
}
if (QRect(skipx, skipy, width, height).contains(point) && _data) {
result.link = _link;
}
if (_parent->getMedia() == this) {
auto fullRight = skipx + width;
auto fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (!bubble && _parent->displayFastShare()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = _parent->fastShareLink();
}
}
}
result.symbol += symbolAdd;
return result;

View file

@ -45,6 +45,10 @@ public:
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
bool allowsFastShare() const override {
return true;
}
~HistoryFileMedia();
protected:
@ -835,6 +839,9 @@ public:
bool customInfoLayout() const override {
return false;
}
bool allowsFastShare() const override {
return true;
}
HistoryMedia *attach() const {
return _attach.get();
@ -946,6 +953,9 @@ public:
bool customInfoLayout() const override {
return false;
}
bool allowsFastShare() const override {
return true;
}
HistoryMedia *attach() const {
return _attach.get();

View file

@ -29,6 +29,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_media_types.h"
#include "history/history_service.h"
#include "auth_session.h"
#include "boxes/share_box.h"
#include "boxes/confirm_box.h"
#include "ui/toast/toast.h"
#include "messenger.h"
#include "styles/style_dialogs.h"
#include "styles/style_widgets.h"
#include "styles/style_history.h"
@ -105,8 +109,178 @@ MTPDmessage::Flags NewForwardedFlags(gsl::not_null<PeerData*> peer, int32 from,
return result;
}
bool HasMediaItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypePhoto:
case MediaTypeVideo:
case MediaTypeFile:
case MediaTypeMusicFile:
case MediaTypeVoiceFile: return true;
case MediaTypeGif: return media->getDocument()->isRoundVideo();
}
}
}
return false;
}
bool HasStickerItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypeSticker: return true;
}
}
}
return false;
}
bool HasGifItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypeGif: return !media->getDocument()->isRoundVideo();
}
}
}
return false;
}
bool HasGameItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypeGame: return true;
}
}
}
return false;
}
bool HasInlineItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (item->viaBot()) {
return true;
}
}
return false;
}
} // namespace
void FastShareMessage(gsl::not_null<HistoryItem*> item) {
struct ShareData {
ShareData(const FullMsgId &msgId) : msgId(msgId) {
}
FullMsgId msgId;
OrderedSet<mtpRequestId> requests;
};
auto data = MakeShared<ShareData>(item->fullId());
auto canCopyLink = item->hasDirectLink();
if (!canCopyLink) {
if (auto bot = item->getMessageBot()) {
if (auto media = item->getMedia()) {
canCopyLink = (media->type() == MediaTypeGame);
}
}
}
auto copyCallback = [data]() {
if (auto main = App::main()) {
if (auto item = App::histItemById(data->msgId)) {
if (item->hasDirectLink()) {
QApplication::clipboard()->setText(item->directLink());
Ui::Toast::Show(lang(lng_channel_public_link_copied));
} else if (auto bot = item->getMessageBot()) {
if (auto media = item->getMedia()) {
if (media->type() == MediaTypeGame) {
auto shortName = static_cast<HistoryGame*>(media)->game()->shortName;
QApplication::clipboard()->setText(Messenger::Instance().createInternalLinkFull(bot->username + qsl("?game=") + shortName));
Ui::Toast::Show(lang(lng_share_game_link_copied));
}
}
}
}
}
};
auto submitCallback = [data](const QVector<PeerData*> &result) {
if (!data->requests.empty()) {
return; // Share clicked already.
}
auto item = App::histItemById(data->msgId);
if (!item || result.empty()) {
return;
}
auto items = SelectedItemSet();
auto restrictedSomewhere = false;
auto restrictedEverywhere = true;
auto firstError = QString();
items.insert(item->id, item);
for_const (auto peer, result) {
auto error = GetErrorTextForForward(peer, items);
if (!error.isEmpty()) {
if (firstError.isEmpty()) {
firstError = error;
}
restrictedSomewhere = true;
continue;
}
restrictedEverywhere = false;
}
if (restrictedEverywhere) {
Ui::show(Box<InformBox>(firstError), KeepOtherLayers);
return;
}
auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) {
if (auto main = App::main()) {
main->sentUpdatesReceived(updates);
}
data->requests.remove(requestId);
if (data->requests.empty()) {
Ui::Toast::Show(lang(lng_share_done));
Ui::hideLayer();
}
};
auto sendFlags = MTPmessages_ForwardMessages::Flag::f_with_my_score;
MTPVector<MTPint> msgIds = MTP_vector<MTPint>(1, MTP_int(data->msgId.msg));
if (auto main = App::main()) {
for_const (auto peer, result) {
if (!GetErrorTextForForward(peer, items).isEmpty()) {
continue;
}
MTPVector<MTPlong> random = MTP_vector<MTPlong>(1, rand_value<MTPlong>());
auto request = MTPmessages_ForwardMessages(MTP_flags(sendFlags), item->history()->peer->input, msgIds, random, peer->input);
auto callback = doneCallback;
auto requestId = MTP::send(request, rpcDone(std::move(callback)));
data->requests.insert(requestId);
}
}
};
auto filterCallback = [](PeerData *peer) {
if (peer->canWrite()) {
if (auto channel = peer->asChannel()) {
return !channel->isBroadcast();
}
return true;
}
return false;
};
auto copyLinkCallback = canCopyLink ? base::lambda<void()>(std::move(copyCallback)) : base::lambda<void()>();
Ui::show(Box<ShareBox>(std::move(copyLinkCallback), std::move(submitCallback), std::move(filterCallback)));
}
void HistoryInitMessages() {
initTextOptions();
}
base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(const FullMsgId &msgId) {
return [dependent = msgId](ChannelData *channel, MsgId msgId) {
if (auto item = App::histItemById(dependent)) {
@ -126,8 +300,25 @@ MTPDmessage::Flags NewMessageFlags(gsl::not_null<PeerData*> peer) {
return result;
}
void HistoryInitMessages() {
initTextOptions();
QString GetErrorTextForForward(gsl::not_null<PeerData*> peer, const SelectedItemSet &items) {
if (!peer->canWrite()) {
return lang(lng_forward_cant);
}
if (auto megagroup = peer->asMegagroup()) {
if (megagroup->restrictedRights().is_send_media() && HasMediaItems(items)) {
return lang(lng_restricted_send_media);
} else if (megagroup->restrictedRights().is_send_stickers() && HasStickerItems(items)) {
return lang(lng_restricted_send_stickers);
} else if (megagroup->restrictedRights().is_send_gifs() && HasGifItems(items)) {
return lang(lng_restricted_send_gifs);
} else if (megagroup->restrictedRights().is_send_games() && HasGameItems(items)) {
return lang(lng_restricted_send_inline);
} else if (megagroup->restrictedRights().is_send_inline() && HasInlineItems(items)) {
return lang(lng_restricted_send_inline);
}
}
return QString();
}
void HistoryMessageVia::create(int32 userId) {
@ -609,6 +800,17 @@ bool HistoryMessage::uploading() const {
return _media && _media->uploading();
}
bool HistoryMessage::displayFastShare() const {
if (_history->peer->isChannel()) {
return !_history->peer->isMegagroup();
} else if (auto user = _history->peer->asUser()) {
if (user->botInfo && !out()) {
return _media && _media->allowsFastShare();
}
}
return false;
}
void HistoryMessage::createComponents(const CreateConfig &config) {
uint64 mask = 0;
if (config.replyTo) {
@ -1476,6 +1678,11 @@ void HistoryMessage::draw(Painter &p, QRect clip, TextSelection selection, TimeM
if (needDrawInfo) {
HistoryMessage::drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayDefault);
}
if (displayFastShare()) {
auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft;
auto fastShareTop = g.top() + g.height() - st::historyFastShareBottom - st::historyFastShareSize;
drawFastShare(p, fastShareLeft, fastShareTop, width());
}
} else if (_media) {
p.translate(g.topLeft());
_media->draw(p, clip.translated(-g.topLeft()), skipTextSelection(selection), ms);
@ -1490,6 +1697,17 @@ void HistoryMessage::draw(Painter &p, QRect clip, TextSelection selection, TimeM
}
}
void HistoryMessage::drawFastShare(Painter &p, int left, int top, int outerWidth) const {
{
p.setPen(Qt::NoPen);
p.setBrush(st::msgServiceBg);
PainterHighQualityEnabler hq(p);
p.drawEllipse(rtlrect(left, top, st::historyFastShareSize, st::historyFastShareSize, outerWidth));
}
st::historyFastShareIcon.paint(p, left, top, outerWidth);
}
void HistoryMessage::paintFromName(Painter &p, QRect &trect, bool selected) const {
if (displayFromName()) {
p.setFont(st::msgNameFont);
@ -1791,6 +2009,13 @@ HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest requ
result.cursor = HistoryInDateCursorState;
}
}
if (displayFastShare()) {
auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft;
auto fastShareTop = g.top() + g.height() - st::historyFastShareBottom - st::historyFastShareSize;
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = fastShareLink();
}
}
} else if (_media) {
result = _media->getState(point - g.topLeft(), request);
result.symbol += _text.length();
@ -1807,6 +2032,17 @@ HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest requ
return result;
}
ClickHandlerPtr HistoryMessage::fastShareLink() const {
if (!_fastShareLink) {
_fastShareLink = MakeShared<LambdaClickHandler>([id = fullId()] {
if (auto item = App::histItemById(id)) {
FastShareMessage(item->toHistoryMessage());
}
});
}
return _fastShareLink;
}
// Forward to _media.
void HistoryMessage::updatePressed(QPoint point) {
if (!_media) return;

View file

@ -23,6 +23,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
void HistoryInitMessages();
base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(const FullMsgId &msgId);
MTPDmessage::Flags NewMessageFlags(gsl::not_null<PeerData*> peer);
QString GetErrorTextForForward(gsl::not_null<PeerData*> peer, const SelectedItemSet &items);
void FastShareMessage(gsl::not_null<HistoryItem*> item);
class HistoryMessage : public HistoryItem, private HistoryItemInstantiated<HistoryMessage> {
public:
@ -67,11 +69,14 @@ public:
}
bool displayEditedBadge(bool hasViaBotOrInlineMarkup) const;
bool uploading() const;
bool displayFastShare() const override;
void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const override;
void drawFastShare(Painter &p, int left, int top, int outerWidth) const override;
void setViewsCount(int32 count) override;
void setId(MsgId newId) override;
void draw(Painter &p, QRect clip, TextSelection selection, TimeMs ms) const override;
ClickHandlerPtr fastShareLink() const override;
void dependencyItemRemoved(HistoryItem *dependency) override;
@ -181,6 +186,8 @@ private:
QString _timeText;
int _timeWidth = 0;
mutable ClickHandlerPtr _fastShareLink;
struct CreateConfig {
MsgId replyTo = 0;
UserId viaBotId = 0;

View file

@ -92,64 +92,6 @@ MTPMessagesFilter TypeToMediaFilter(MediaOverviewType &type) {
}
}
bool HasMediaItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypePhoto:
case MediaTypeVideo:
case MediaTypeFile:
case MediaTypeMusicFile:
case MediaTypeVoiceFile: return true;
case MediaTypeGif: return media->getDocument()->isRoundVideo();
}
}
}
return false;
}
bool HasStickerItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypeSticker: return true;
}
}
}
return false;
}
bool HasGifItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypeGif: return !media->getDocument()->isRoundVideo();
}
}
}
return false;
}
bool HasGameItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypeGame: return true;
}
}
}
return false;
}
bool HasInlineItems(const SelectedItemSet &items) {
for_const (auto item, items) {
if (item->viaBot()) {
return true;
}
}
return false;
}
} // namespace
StackItemSection::StackItemSection(std::unique_ptr<Window::SectionMemento> &&memento) : StackItem(nullptr)
@ -596,26 +538,10 @@ bool MainWidget::setForwardDraft(PeerId peerId, ForwardWhatMessages what) {
bool MainWidget::setForwardDraft(PeerId peerId, const SelectedItemSet &items) {
Expects(peerId != 0);
auto peer = App::peer(peerId);
auto finishWithError = [this](const QString &error) {
auto error = GetErrorTextForForward(peer, items);
if (!error.isEmpty()) {
Ui::show(Box<InformBox>(error));
return false;
};
if (!peer->canWrite()) {
return finishWithError(lang(lng_forward_cant));
}
if (auto megagroup = peer->asMegagroup()) {
if (megagroup->restrictedRights().is_send_media() && HasMediaItems(items)) {
return finishWithError(lang(lng_restricted_send_media));
} else if (megagroup->restrictedRights().is_send_stickers() && HasStickerItems(items)) {
return finishWithError(lang(lng_restricted_send_stickers));
} else if (megagroup->restrictedRights().is_send_gifs() && HasGifItems(items)) {
return finishWithError(lang(lng_restricted_send_gifs));
} else if (megagroup->restrictedRights().is_send_games() && HasGameItems(items)) {
return finishWithError(lang(lng_restricted_send_inline));
} else if (megagroup->restrictedRights().is_send_inline() && HasInlineItems(items)) {
return finishWithError(lang(lng_restricted_send_inline));
}
}
App::history(peer)->setForwardDraft(items);

View file

@ -35,7 +35,17 @@ NeverFreedPointer<QMap<QObject*, Manager*>> _managers;
Manager::Manager(QWidget *parent) : QObject(parent) {
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideTimeout()));
connect(parent, SIGNAL(resized()), this, SLOT(onParentResized()));
}
bool Manager::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Resize) {
for (auto i = _toastByWidget.cbegin(), e = _toastByWidget.cend(); i != e; ++i) {
if (i.key()->parentWidget() == o) {
i.key()->onParentResized();
}
}
}
return QObject::eventFilter(o, e);
}
Manager *Manager::instance(QWidget *parent) {
@ -58,7 +68,23 @@ void Manager::addToast(std::unique_ptr<Instance> &&toast) {
_toastByWidget.insert(widget, t);
connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onToastWidgetDestroyed(QObject*)));
connect(widget->parentWidget(), SIGNAL(resized(QSize)), this, SLOT(onToastWidgetParentResized()), Qt::UniqueConnection);
if (auto parent = widget->parentWidget()) {
auto found = false;
for (auto i = _toastParents.begin(); i != _toastParents.cend();) {
if (*i == parent) {
found = true;
break;
} else if (!*i) {
i = _toastParents.erase(i);
} else {
++i;
}
}
if (!found) {
_toastParents.insert(parent);
parent->installEventFilter(this);
}
}
auto oldHideNearestMs = _toastByHideTime.isEmpty() ? 0LL : _toastByHideTime.firstKey();
_toastByHideTime.insert(t->_hideAtMs, t);
@ -96,17 +122,6 @@ void Manager::onToastWidgetDestroyed(QObject *widget) {
}
}
void Manager::onToastWidgetParentResized() {
auto resizedWidget = QObject::sender();
if (!resizedWidget) return;
for (auto i = _toastByWidget.cbegin(), e = _toastByWidget.cend(); i != e; ++i) {
if (i.key()->parentWidget() == resizedWidget) {
i.key()->onParentResized();
}
}
}
void Manager::startNextHideTimer() {
if (_toastByHideTime.isEmpty()) return;

View file

@ -41,10 +41,12 @@ public:
~Manager();
protected:
bool eventFilter(QObject *o, QEvent *e);
private slots:
void onHideTimeout();
void onToastWidgetDestroyed(QObject *widget);
void onToastWidgetParentResized();
private:
Manager(QWidget *parent);
@ -56,6 +58,7 @@ private:
QMultiMap<TimeMs, Instance*> _toastByHideTime;
QMap<Widget*, Instance*> _toastByWidget;
QList<Instance*> _toasts;
OrderedSet<QPointer<QWidget>> _toastParents;
};