Encapsulated inline bot result class. Started inline bot

result downloading by external links, not ready (at all).
This commit is contained in:
John Preston 2016-04-06 00:24:27 +04:00
parent 1e72c8a89b
commit 3be34a4bb7
11 changed files with 536 additions and 280 deletions

View file

@ -832,6 +832,49 @@ private:
};
// This pointer is used for static non-POD variables that are allocated
// on first use by constructor and are never automatically freed.
template <typename T>
class StaticNeverFreedPointer {
public:
explicit StaticNeverFreedPointer(T *p) : _p(p) {
}
StaticNeverFreedPointer(const StaticNeverFreedPointer<T> &other) = delete;
StaticNeverFreedPointer &operator=(const StaticNeverFreedPointer<T> &other) = delete;
T *data() const {
return _p;
}
T *release() {
return getPointerAndReset(_p);
}
void reset(T *p = nullptr) {
delete _p;
_p = p;
}
bool isNull() const {
return data() == nullptr;
}
void clear() {
reset();
}
T *operator->() const {
return data();
}
T &operator*() const {
t_assert(!isNull());
return *data();
}
explicit operator bool() const {
return !isNull();
}
private:
T *_p = nullptr;
};
template <typename I>
inline void destroyImplementation(I *&ptr) {
if (ptr) {

View file

@ -1458,82 +1458,25 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
}
InlineItem *item = _inlineRows.at(row).items.at(col);
PhotoData *photo = item->getPhoto();
DocumentData *document = item->getDocument();
InlineResult *inlineResult = item->getResult();
using Type = InlineResult::Type;
auto getShownPhoto = [photo, inlineResult]() -> PhotoData* {
if (photo) {
return photo;
} else if (inlineResult && inlineResult->type == Type::Photo) {
return inlineResult->photo;
}
return nullptr;
};
auto getShownDocument = [document, inlineResult]() -> DocumentData* {
auto inlineResultIsFileType = [](InlineResult *inlineResult) {
return inlineResult->type == Type::Video ||
inlineResult->type == Type::Audio ||
inlineResult->type == Type::Sticker ||
inlineResult->type == Type::File ||
inlineResult->type == Type::Gif;
};
if (document) {
return document;
} else if (inlineResult && inlineResultIsFileType(inlineResult)) {
return inlineResult->document;
}
return nullptr;
};
auto sendInlineItem = [photo, document, inlineResult, this]() -> void {
if (photo) {
if (PhotoData *photo = item->getPhoto()) {
if (photo->medium->loaded() || photo->thumb->loaded()) {
emit selected(photo);
} else if (document) {
} else if (!photo->medium->loading()) {
photo->thumb->loadEvenCancelled();
photo->medium->loadEvenCancelled();
}
} else if (DocumentData *document = item->getDocument()) {
if (document->loaded()) {
emit selected(document);
} else if (inlineResult) {
} else if (document->loading()) {
document->cancel();
} else {
DocumentOpenClickHandler::doOpen(document, ActionOnLoadNone);
}
} else if (InlineResult *inlineResult = item->getResult()) {
if (inlineResult->onChoose(item)) {
emit selected(inlineResult, _inlineBot);
}
};
if (PhotoData *shownPhoto = getShownPhoto()) {
if (shownPhoto->medium->loaded() || shownPhoto->thumb->loaded()) {
sendInlineItem();
} else if (!shownPhoto->medium->loading()) {
shownPhoto->thumb->loadEvenCancelled();
shownPhoto->medium->loadEvenCancelled();
}
} else if (DocumentData *shownDocument = getShownDocument()) {
if (!inlineResult || inlineResult->type == Type::Gif) {
if (shownDocument->loaded()) {
sendInlineItem();
} else if (shownDocument->loading()) {
shownDocument->cancel();
} else {
DocumentOpenClickHandler::doOpen(shownDocument, ActionOnLoadNone);
}
} else {
sendInlineItem();
}
} else if (inlineResult) {
if (inlineResult->type == Type::Photo) {
if (inlineResult->thumb->loaded()) {
sendInlineItem();
} else if (!inlineResult->thumb->loading()) {
inlineResult->thumb->loadEvenCancelled();
Ui::repaintInlineItem(item);
}
} else if (inlineResult->type == Type::Gif) {
if (inlineResult->loaded()) {
sendInlineItem();
} else if (inlineResult->loading()) {
inlineResult->cancelFile();
Ui::repaintInlineItem(item);
} else {
inlineResult->saveFile(QString(), LoadFromCloudOrLocal, false);
Ui::repaintInlineItem(item);
}
} else {
sendInlineItem();
}
}
return;
}
@ -1655,12 +1598,7 @@ void StickerPanInner::hideFinish(bool completely) {
photo->forget();
}
if (InlineResult *result = item->getResult()) {
if (DocumentData *document = result->document) {
document->forget();
}
if (PhotoData *photo = result->photo) {
photo->forget();
}
result->forget();
}
};
clearInlineRows(false);

View file

@ -6775,7 +6775,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot
result->addToHistory(_history, flags, messageId, messageFromId, messageDate, messageViaBotId, replyToId());
_history->sendRequestId = MTP::send(MTPmessages_SendInlineBotResult(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_long(randomId), MTP_long(result->queryId), MTP_string(result->id)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
_history->sendRequestId = MTP::send(MTPmessages_SendInlineBotResult(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_long(randomId), MTP_long(result->getQueryId()), MTP_string(result->getId())), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _broadcast.checked(), _silent.checked());
cancelReply(lastKeyboardUsed);

View file

@ -38,10 +38,8 @@ FileBase::FileBase(DocumentData *document) : ItemBase(document) {
DocumentData *FileBase::getShownDocument() const {
if (DocumentData *result = getDocument()) {
return result;
} else if (Result *result = getResult()) {
return result->document;
}
return nullptr;
return getResultDocument();
}
int FileBase::content_width() const {
@ -52,10 +50,8 @@ int FileBase::content_width() const {
if (!document->thumb->isNull()) {
return convertScale(document->thumb->width());
}
} else if (_result) {
return _result->width;
}
return 0;
return getResultWidth();
}
int FileBase::content_height() const {
@ -66,10 +62,8 @@ int FileBase::content_height() const {
if (!document->thumb->isNull()) {
return convertScale(document->thumb->height());
}
} else if (_result) {
return _result->height;
}
return 0;
return getResultHeight();
}
bool FileBase::content_loading() const {
@ -136,23 +130,20 @@ ImagePtr FileBase::content_thumb() const {
return document->thumb;
}
}
if (_result->photo && !_result->photo->thumb->isNull()) {
return _result->photo->thumb;
}
return _result->thumb;
return getResultThumb();
}
int FileBase::content_duration() const {
if (_result->document) {
if (_result->document->duration() > 0) {
return _result->document->duration();
} else if (SongData *song = _result->document->song()) {
if (DocumentData *document = getShownDocument()) {
if (document->duration() > 0) {
return document->duration();
} else if (SongData *song = document->song()) {
if (song->duration) {
return song->duration;
}
}
}
return _result->duration;
return getResultDuration();
}
Gif::Gif(Result *result) : FileBase(result) {
@ -354,13 +345,14 @@ void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const {
}
}
} else {
if (!_result->thumb_url.isEmpty()) {
if (_result->thumb->loaded()) {
ImagePtr thumb = getResultThumb();
if (!thumb->isNull()) {
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = _result->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
_thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
_result->thumb->load();
thumb->load();
}
}
}
@ -437,8 +429,11 @@ void Sticker::preload() const {
} else {
document->checkSticker();
}
} else if (!_result->thumb->isNull()) {
_result->thumb->load();
} else {
ImagePtr thumb = getResultThumb();
if (!thumb->isNull()) {
thumb->load();
}
}
}
@ -506,14 +501,15 @@ void Sticker::prepareThumb() const {
_thumbLoaded = true;
}
} else {
if (_result->thumb->loaded()) {
ImagePtr thumb = getResultThumb();
if (thumb->loaded()) {
if (!_thumbLoaded) {
QSize thumbSize = getThumbSize();
_thumb = _result->thumb->pix(thumbSize.width(), thumbSize.height());
_thumb = thumb->pix(thumbSize.width(), thumbSize.height());
_thumbLoaded = true;
}
} else {
_result->thumb->load();
thumb->load();
}
}
}
@ -557,10 +553,8 @@ void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x,
PhotoData *Photo::getShownPhoto() const {
if (PhotoData *result = getPhoto()) {
return result;
} else if (Result *result = getResult()) {
return result->photo;
}
return nullptr;
return getResultPhoto();
}
QSize Photo::countFrameSize() const {
@ -605,12 +599,13 @@ void Photo::prepareThumb(int32 width, int32 height, const QSize &frame) const {
photo->medium->load();
}
} else {
if (_result->thumb->loaded()) {
ImagePtr thumb = getResultThumb();
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = _result->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
_thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
_result->thumb->load();
thumb->load();
}
}
}
@ -618,19 +613,15 @@ void Photo::prepareThumb(int32 width, int32 height, const QSize &frame) const {
int Photo::content_width() const {
if (PhotoData *photo = getShownPhoto()) {
return photo->full->width();
} else if (_result) {
return _result->width;
}
return 0;
return getResultWidth();
}
int Photo::content_height() const {
if (PhotoData *photo = getShownPhoto()) {
return photo->full->height();
} else if (_result) {
return _result->height;
}
return 0;
return getResultHeight();
}
bool Photo::content_loaded() const {
@ -649,11 +640,9 @@ void Photo::content_forget() {
}
Video::Video(Result *result) : FileBase(result)
, _link(getResultContentUrlHandler())
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
if (!result->content_url.isEmpty()) {
_link = clickHandlerFromUrl(result->content_url);
}
if (int duration = content_duration()) {
_duration = formatDurationText(duration);
}
@ -749,7 +738,7 @@ void Video::prepareThumb(int32 width, int32 height) const {
}
void OpenFileClickHandler::onClickImpl() const {
_result->automaticLoadGif();
_result->openFile();
}
void CancelFileClickHandler::onClickImpl() const {
@ -907,52 +896,23 @@ void File::checkAnimationFinished() {
}
Article::Article(Result *result, bool withThumb) : ItemBase(result)
, _url(getResultUrlHandler())
, _link(getResultContentUrlHandler())
, _withThumb(withThumb)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
if (!result->url.isEmpty()) {
_url = clickHandlerFromUrl(result->url);
}
if (!result->content_url.isEmpty()) {
_link = clickHandlerFromUrl(result->content_url);
} else {
LocationCoords location;
if (result->getLocationCoords(&location)) {
_link.reset(new LocationClickHandler(location));
int32 w = st::inlineThumbSize, h = st::inlineThumbSize;
int32 zoom = 13, scale = 1;
if (cScale() == dbisTwo || cRetina()) {
scale = 2;
w /= 2;
h /= 2;
}
QString coords = qsl("%1,%2").arg(location.lat).arg(location.lon);
QString url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + coords + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + coords + qsl("&sensor=false");
result->thumb = ImagePtr(url);
}
}
QVector<QStringRef> parts = _result->url.splitRef('/');
if (!parts.isEmpty()) {
QStringRef domain = parts.at(0);
if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others
domain = parts.at(2);
}
parts = domain.split('@').back().split('.');
if (parts.size() > 1) {
_letter = parts.at(parts.size() - 2).at(0).toUpper();
}
}
if (_letter.isEmpty() && !_result->title.isEmpty()) {
_letter = _result->title.at(0).toUpper();
LocationCoords location;
if (!_link && result->getLocationCoords(&location)) {
_link.reset(new LocationClickHandler(location));
}
_thumbLetter = getResultThumbLetter();
}
void Article::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldFont, textOneLine(_result->title), titleOpts);
_title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = (_withThumb || _url) ? 2 : 3;
@ -970,10 +930,10 @@ void Article::initDimensions() {
int32 Article::resizeGetHeight(int32 width) {
_width = qMin(width, _maxw);
if (_url) {
_urlText = _result->url;
_urlText = getResultUrl();
_urlWidth = st::normalFont->width(_urlText);
if (_urlWidth > _width - st::inlineThumbSize - st::inlineThumbSkip) {
_urlText = st::normalFont->elided(_result->url, _width - st::inlineThumbSize - st::inlineThumbSkip);
_urlText = st::normalFont->elided(_urlText, _width - st::inlineThumbSize - st::inlineThumbSkip);
_urlWidth = st::normalFont->width(_urlText);
}
}
@ -988,15 +948,16 @@ void Article::paint(Painter &p, const QRect &clip, uint32 selection, const Paint
prepareThumb(st::inlineThumbSize, st::inlineThumbSize);
QRect rthumb(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width));
if (_thumb.isNull()) {
if (_result->thumb->isNull() && !_letter.isEmpty()) {
int32 index = (_letter.at(0).unicode() % 4);
ImagePtr thumb = getResultThumb();
if (thumb->isNull() && !_thumbLetter.isEmpty()) {
int32 index = (_thumbLetter.at(0).unicode() % 4);
style::color colors[] = { st::msgFileRedColor, st::msgFileYellowColor, st::msgFileGreenColor, st::msgFileBlueColor };
p.fillRect(rthumb, colors[index]);
if (!_letter.isEmpty()) {
if (!_thumbLetter.isEmpty()) {
p.setFont(st::linksLetterFont);
p.setPen(st::white);
p.drawText(rthumb, _letter, style::al_center);
p.drawText(rthumb, _thumbLetter, style::al_center);
}
} else {
p.fillRect(rthumb, st::overviewPhotoBg);
@ -1047,18 +1008,17 @@ void Article::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32
}
void Article::prepareThumb(int32 width, int32 height) const {
if (_result->thumb->isNull()) {
if (_result->type == Result::Type::Contact) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = userDefPhoto(qHash(_result->id) % UserColorsCount)->pixCircled(width, height);
}
ImagePtr thumb = getResultThumb();
if (thumb->isNull()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = getResultContactAvatar(width, height);
}
return;
}
if (_result->thumb->loaded()) {
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int32 w = qMax(convertScale(_result->thumb->width()), 1), h = qMax(convertScale(_result->thumb->height()), 1);
int32 w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
@ -1070,10 +1030,10 @@ void Article::prepareThumb(int32 width, int32 height) const {
w = width;
}
}
_thumb = _result->thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
_thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
_result->thumb->load();
thumb->load();
}
}

View file

@ -313,7 +313,7 @@ private:
bool _withThumb;
mutable QPixmap _thumb;
Text _title, _description;
QString _letter, _urlText;
QString _thumbLetter, _urlText;
int32 _urlWidth;
void prepareThumb(int32 width, int32 height) const;

View file

@ -51,12 +51,12 @@ PhotoData *ItemBase::getPhoto() const {
void ItemBase::preload() const {
if (_result) {
if (_result->photo) {
_result->photo->thumb->load();
} else if (_result->document) {
_result->document->thumb->load();
} else if (!_result->thumb->isNull()) {
_result->thumb->load();
if (_result->_photo) {
_result->_photo->thumb->load();
} else if (_result->_document) {
_result->_document->thumb->load();
} else if (!_result->_thumb->isNull()) {
_result->_thumb->load();
}
} else if (_doc) {
_doc->thumb->load();
@ -74,7 +74,7 @@ void ItemBase::update() {
UniquePointer<ItemBase> ItemBase::createLayout(Result *result, bool forceThumb) {
using Type = Result::Type;
switch (result->type) {
switch (result->_type) {
case Type::Photo: return MakeUnique<internal::Photo>(result); break;
case Type::Audio:
case Type::File: return MakeUnique<internal::File>(result); break;
@ -92,5 +92,82 @@ UniquePointer<ItemBase> ItemBase::createLayoutGif(DocumentData *document) {
return MakeUnique<internal::Gif>(document, true);
}
DocumentData *ItemBase::getResultDocument() const {
return _result ? _result->_document : nullptr;
}
PhotoData *ItemBase::getResultPhoto() const {
return _result ? _result->_photo : nullptr;
}
int ItemBase::getResultWidth() const {
return _result ? _result->_width : 0;
}
int ItemBase::getResultHeight() const {
return _result ? _result->_height : 0;
}
ImagePtr ItemBase::getResultThumb() const {
if (_result) {
if (_result->_photo && !_result->_photo->thumb->isNull()) {
return _result->_photo->thumb;
}
if (!_result->_thumb->isNull()) {
return _result->_thumb;
}
return _result->_locationThumb;
}
return ImagePtr();
}
QPixmap ItemBase::getResultContactAvatar(int width, int height) const {
if (_result->_type == Result::Type::Contact) {
return userDefPhoto(qHash(_result->_id) % UserColorsCount)->pixCircled(width, height);
}
return QPixmap();
}
int ItemBase::getResultDuration() const {
return _result->_duration;
}
QString ItemBase::getResultUrl() const {
return _result->_url;
}
ClickHandlerPtr ItemBase::getResultUrlHandler() const {
if (!_result->_url.isEmpty()) {
return clickHandlerFromUrl(_result->_url);
}
return ClickHandlerPtr();
}
ClickHandlerPtr ItemBase::getResultContentUrlHandler() const {
if (!_result->_content_url.isEmpty()) {
return clickHandlerFromUrl(_result->_content_url);
}
return ClickHandlerPtr();
}
QString ItemBase::getResultThumbLetter() const {
QVector<QStringRef> parts = _result->_url.splitRef('/');
if (!parts.isEmpty()) {
QStringRef domain = parts.at(0);
if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others
domain = parts.at(2);
}
parts = domain.split('@').back().split('.');
if (parts.size() > 1) {
return parts.at(parts.size() - 2).at(0).toUpper();
}
}
if (!_result->_title.isEmpty()) {
return _result->_title.at(0).toUpper();
}
return QString();
}
} // namespace Layout
} // namespace InlineBots

View file

@ -89,6 +89,18 @@ public:
static UniquePointer<ItemBase> createLayoutGif(DocumentData *document);
protected:
DocumentData *getResultDocument() const;
PhotoData *getResultPhoto() const;
int getResultWidth() const;
int getResultHeight() const;
ImagePtr getResultThumb() const;
QPixmap getResultContactAvatar(int width, int height) const;
int getResultDuration() const;
QString getResultUrl() const;
ClickHandlerPtr getResultUrlHandler() const;
ClickHandlerPtr getResultContentUrlHandler() const;
QString getResultThumbLetter() const;
Result *_result = nullptr;
DocumentData *_doc = nullptr;
PhotoData *_photo = nullptr;

View file

@ -56,18 +56,12 @@ Result *getResultFromLoader(FileLoader *loader) {
return ResultsByLoader->value(loader, nullptr);
}
namespace {
using StringToTypeMap = QMap<QString, Result::Type>;
NeverFreedPointer<StringToTypeMap> stringToTypeMap;
} // namespace
Result::Result(const Creator &creator) : queryId(creator.queryId), type(creator.type) {
Result::Result(const Creator &creator) : _queryId(creator.queryId), _type(creator.type) {
}
UniquePointer<Result> Result::create(uint64 queryId, const MTPBotInlineResult &mtpData) {
stringToTypeMap.createIfNull([]() -> StringToTypeMap* {
using StringToTypeMap = QMap<QString, Result::Type>;
StaticNeverFreedPointer<StringToTypeMap> stringToTypeMap{ ([]() -> StringToTypeMap* {
auto result = MakeUnique<StringToTypeMap>();
result->insert(qsl("photo"), Result::Type::Photo);
result->insert(qsl("video"), Result::Type::Video);
@ -79,8 +73,9 @@ UniquePointer<Result> Result::create(uint64 queryId, const MTPBotInlineResult &m
result->insert(qsl("contact"), Result::Type::Contact);
result->insert(qsl("venue"), Result::Type::Venue);
return result.release();
});
auto getInlineResultType = [](const MTPBotInlineResult &inlineResult) -> Type {
})() };
auto getInlineResultType = [&stringToTypeMap](const MTPBotInlineResult &inlineResult) -> Type {
QString type;
switch (inlineResult.type()) {
case mtpc_botInlineResult: type = qs(inlineResult.c_botInlineResult().vtype); break;
@ -98,32 +93,38 @@ UniquePointer<Result> Result::create(uint64 queryId, const MTPBotInlineResult &m
switch (mtpData.type()) {
case mtpc_botInlineResult: {
const MTPDbotInlineResult &r(mtpData.c_botInlineResult());
result->id = qs(r.vid);
if (r.has_title()) result->title = qs(r.vtitle);
if (r.has_description()) result->description = qs(r.vdescription);
if (r.has_url()) result->url = qs(r.vurl);
if (r.has_thumb_url()) result->thumb_url = qs(r.vthumb_url);
if (r.has_content_type()) result->content_type = qs(r.vcontent_type);
if (r.has_content_url()) result->content_url = qs(r.vcontent_url);
if (r.has_w()) result->width = r.vw.v;
if (r.has_h()) result->height = r.vh.v;
if (r.has_duration()) result->duration = r.vduration.v;
if (!result->thumb_url.isEmpty() && (result->thumb_url.startsWith(qstr("http://"), Qt::CaseInsensitive) || result->thumb_url.startsWith(qstr("https://"), Qt::CaseInsensitive))) {
result->thumb = ImagePtr(result->thumb_url);
result->_id = qs(r.vid);
if (r.has_title()) result->_title = qs(r.vtitle);
if (r.has_description()) result->_description = qs(r.vdescription);
if (r.has_url()) result->_url = qs(r.vurl);
if (r.has_thumb_url()) result->_thumb_url = qs(r.vthumb_url);
if (r.has_content_type()) result->_content_type = qs(r.vcontent_type);
if (r.has_content_url()) result->_content_url = qs(r.vcontent_url);
if (r.has_w()) result->_width = r.vw.v;
if (r.has_h()) result->_height = r.vh.v;
if (r.has_duration()) result->_duration = r.vduration.v;
if (!result->_thumb_url.isEmpty() && (result->_thumb_url.startsWith(qstr("http://"), Qt::CaseInsensitive) || result->_thumb_url.startsWith(qstr("https://"), Qt::CaseInsensitive))) {
result->_thumb = ImagePtr(result->_thumb_url);
}
message = &r.vsend_message;
} break;
case mtpc_botInlineMediaResult: {
const MTPDbotInlineMediaResult &r(mtpData.c_botInlineMediaResult());
result->id = qs(r.vid);
if (r.has_title()) result->title = qs(r.vtitle);
if (r.has_description()) result->description = qs(r.vdescription);
if (r.has_photo()) result->photo = App::feedPhoto(r.vphoto);
if (r.has_document()) result->document = App::feedDocument(r.vdocument);
result->_id = qs(r.vid);
if (r.has_title()) result->_title = qs(r.vtitle);
if (r.has_description()) result->_description = qs(r.vdescription);
if (r.has_photo()) {
result->_mtpPhoto = r.vphoto;
result->_photo = App::feedPhoto(r.vphoto);
}
if (r.has_document()) {
result->_mtpDocument = r.vdocument;
result->_document = App::feedDocument(r.vdocument);
}
message = &r.vsend_message;
} break;
}
bool badAttachment = (result->photo && !result->photo->access) || (result->document && !result->document->access);
bool badAttachment = (result->_photo && !result->_photo->access) || (result->_document && !result->_document->access);
if (!message) {
return UniquePointer<Result>();
@ -132,10 +133,10 @@ UniquePointer<Result> Result::create(uint64 queryId, const MTPBotInlineResult &m
switch (message->type()) {
case mtpc_botInlineMessageMediaAuto: {
const MTPDbotInlineMessageMediaAuto &r(message->c_botInlineMessageMediaAuto());
if (result->type == Type::Photo) {
result->sendData.reset(new internal::SendPhoto(result->photo, result->content_url, qs(r.vcaption)));
if (result->_type == Type::Photo) {
result->sendData.reset(new internal::SendPhoto(result->_photo, result->_content_url, qs(r.vcaption)));
} else {
result->sendData.reset(new internal::SendFile(result->document, result->content_url, qs(r.vcaption)));
result->sendData.reset(new internal::SendFile(result->_document, result->_content_url, qs(r.vcaption)));
}
} break;
@ -176,11 +177,81 @@ UniquePointer<Result> Result::create(uint64 queryId, const MTPBotInlineResult &m
if (badAttachment || !result->sendData || !result->sendData->isValid()) {
return UniquePointer<Result>();
}
LocationCoords location;
if (result->getLocationCoords(&location)) {
int32 w = st::inlineThumbSize, h = st::inlineThumbSize;
int32 zoom = 13, scale = 1;
if (cScale() == dbisTwo || cRetina()) {
scale = 2;
w /= 2;
h /= 2;
}
QString coords = qsl("%1,%2").arg(location.lat).arg(location.lon);
QString url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + coords + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + coords + qsl("&sensor=false");
result->_locationThumb = ImagePtr(url);
}
return result;
}
bool Result::onChoose(Layout::ItemBase *layout) {
if (_photo && _type == Type::Photo) {
if (_photo->medium->loaded() || _photo->thumb->loaded()) {
return true;
} else if (!_photo->medium->loading()) {
_photo->thumb->loadEvenCancelled();
_photo->medium->loadEvenCancelled();
}
}
if (_document && (
_type == Type::Video ||
_type == Type::Audio ||
_type == Type::Sticker ||
_type == Type::File ||
_type == Type::Gif)) {
if (_type == Type::Gif) {
if (_document->loaded()) {
return true;
} else if (_document->loading()) {
_document->cancel();
} else {
DocumentOpenClickHandler::doOpen(_document, ActionOnLoadNone);
}
} else {
return true;
}
}
if (_type == Type::Photo) {
if (_thumb->loaded()) {
return true;
} else if (!_thumb->loading()) {
_thumb->loadEvenCancelled();
Ui::repaintInlineItem(layout);
}
} else if (_type == Type::Gif) {
if (loaded()) {
return true;
} else if (loading()) {
cancelFile();
Ui::repaintInlineItem(layout);
} else {
saveFile(QString(), LoadFromCloudOrLocal, false);
Ui::repaintInlineItem(layout);
}
} else {
return true;
}
return false;
}
void Result::automaticLoadGif() {
if (loaded() || type != Type::Gif || (content_type != qstr("video/mp4") && content_type != "image/gif")) return;
if (loaded() || _type != Type::Gif) {
return;
}
if (_content_type != qstr("video/mp4") && _content_type != qstr("image/gif")) {
return;
}
if (_loader != CancelledWebFileLoader) {
// if load at least anywhere
@ -191,7 +262,7 @@ void Result::automaticLoadGif() {
void Result::automaticLoadSettingsChangedGif() {
if (loaded() || _loader != CancelledWebFileLoader) return;
_loader = 0;
_loader = nullptr;
}
void Result::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading) {
@ -199,18 +270,18 @@ void Result::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, boo
return;
}
if (_loader == CancelledWebFileLoader) _loader = 0;
if (_loader == CancelledWebFileLoader) _loader = nullptr;
if (_loader) {
if (!_loader->setFileName(toFile)) {
cancelFile();
_loader = 0;
_loader = nullptr;
}
}
if (_loader) {
if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud();
} else {
_loader = new webFileLoader(content_url, toFile, fromCloud, autoLoading);
_loader = new webFileLoader(_content_url, toFile, fromCloud, autoLoading);
regLoader(_loader, this);
_loader->connect(_loader, SIGNAL(progress(FileLoader*)), App::main(), SLOT(inlineResultLoadProgress(FileLoader*)));
@ -219,6 +290,80 @@ void Result::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, boo
}
}
void Result::openFile() {
//if (loaded()) {
// bool playVoice = data->voice() && audioPlayer() && item;
// bool playMusic = data->song() && audioPlayer() && item;
// bool playAnimation = data->isAnimation() && item && item->getMedia();
// const FileLocation &location(data->location(true));
// if (!location.isEmpty() || (!data->data().isEmpty() && (playVoice || playMusic || playAnimation))) {
// if (playVoice) {
// AudioMsgId playing;
// AudioPlayerState playingState = AudioPlayerStopped;
// audioPlayer()->currentState(&playing, &playingState);
// if (playing.msgId == item->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
// audioPlayer()->pauseresume(OverviewVoiceFiles);
// } else {
// AudioMsgId audio(data, item->fullId());
// audioPlayer()->play(audio);
// if (App::main()) {
// App::main()->audioPlayProgress(audio);
// App::main()->mediaMarkRead(data);
// }
// }
// } else if (playMusic) {
// SongMsgId playing;
// AudioPlayerState playingState = AudioPlayerStopped;
// audioPlayer()->currentState(&playing, &playingState);
// if (playing.msgId == item->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
// audioPlayer()->pauseresume(OverviewFiles);
// } else {
// SongMsgId song(data, item->fullId());
// audioPlayer()->play(song);
// if (App::main()) App::main()->documentPlayProgress(song);
// }
// } else if (data->voice() || data->isVideo()) {
// psOpenFile(location.name());
// if (App::main()) App::main()->mediaMarkRead(data);
// } else if (data->size < MediaViewImageSizeLimit) {
// if (!data->data().isEmpty() && playAnimation) {
// if (action == ActionOnLoadPlayInline && item->getMedia()) {
// item->getMedia()->playInline(item);
// } else {
// App::wnd()->showDocument(data, item);
// }
// } else if (location.accessEnable()) {
// if (item && (data->isAnimation() || QImageReader(location.name()).canRead())) {
// if (action == ActionOnLoadPlayInline && item->getMedia()) {
// item->getMedia()->playInline(item);
// } else {
// App::wnd()->showDocument(data, item);
// }
// } else {
// psOpenFile(location.name());
// }
// location.accessDisable();
// } else {
// psOpenFile(location.name());
// }
// } else {
// psOpenFile(location.name());
// }
// return;
// }
//}
//QString filename = documentSaveFilename(data);
//if (filename.isEmpty()) return;
//if (!data->saveToCache()) {
// filename = documentSaveFilename(data);
// if (filename.isEmpty()) return;
//}
//saveFile()
//data->save(filename, action, item ? item->fullId() : FullMsgId());
}
void Result::cancelFile() {
if (!loading()) return;
@ -254,7 +399,7 @@ bool Result::loaded() const {
_loader->deleteLater();
_loader->stop();
_loader = 0;
_loader = nullptr;
}
}
return !_data.isEmpty();
@ -265,8 +410,14 @@ bool Result::displayLoading() const {
}
void Result::forget() {
thumb->forget();
_thumb->forget();
_data.clear();
if (_document) {
_document->forget();
}
if (_photo) {
_photo->forget();
}
}
float64 Result::progress() const {
@ -274,10 +425,10 @@ float64 Result::progress() const {
}
bool Result::hasThumbDisplay() const {
if (!thumb->isNull()) {
if (!_thumb->isNull()) {
return true;
}
if (type == Type::Contact) {
if (_type == Type::Contact) {
return true;
}
if (sendData->hasLocationCoords()) {

View file

@ -42,18 +42,6 @@ private:
struct Creator;
public:
enum class Type {
Unknown,
Photo,
Video,
Audio,
Sticker,
File,
Gif,
Article,
Contact,
Venue,
};
// Constructor is public only for MakeUnique<>() to work.
// You should use create() static method instead.
@ -62,22 +50,21 @@ public:
Result(const Result &other) = delete;
Result &operator=(const Result &other) = delete;
uint64 queryId;
QString id;
Type type;
DocumentData *document = nullptr;
PhotoData *photo = nullptr;
QString title, description, url, thumb_url;
QString content_type, content_url;
int width = 0;
int height = 0;
int duration = 0;
uint64 getQueryId() const {
return _queryId;
}
QString getId() const {
return _id;
}
ImagePtr thumb;
// This is real SendClickHandler::onClick implementation for the specified
// inline bot result. If it returns true you need to send this result.
bool onChoose(Layout::ItemBase *layout);
void automaticLoadGif();
void automaticLoadSettingsChangedGif();
void saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading);
void openFile();
void cancelFile();
QByteArray data() const;
@ -99,11 +86,43 @@ public:
~Result();
private:
enum class Type {
Unknown,
Photo,
Video,
Audio,
Sticker,
File,
Gif,
Article,
Contact,
Venue,
};
friend class internal::SendData;
friend class Layout::ItemBase;
struct Creator {
uint64 queryId;
Type type;
};
uint64 _queryId = 0;
QString _id;
Type _type = Type::Unknown;
QString _title, _description, _url, _thumb_url;
QString _content_type, _content_url;
int _width = 0;
int _height = 0;
int _duration = 0;
mutable MTPDocument _mtpDocument = MTP_documentEmpty(MTP_long(0));
DocumentData *_document = nullptr;
mutable MTPPhoto _mtpPhoto = MTP_photoEmpty(MTP_long(0));
PhotoData *_photo = nullptr;
ImagePtr _thumb, _locationThumb;
UniquePointer<internal::SendData> sendData;
QByteArray _data;

View file

@ -28,11 +28,59 @@ namespace InlineBots {
namespace internal {
QString SendData::getLayoutTitle(const Result *owner) const {
return owner->title;
return owner->_title;
}
QString SendData::getLayoutDescription(const Result *owner) const {
return owner->description;
return owner->_description;
}
ImagePtr SendData::getResultThumb(const Result *owner) const {
return owner->_thumb;
}
int SendData::getResultWidth(const Result *owner) const {
return owner->_width;
}
int SendData::getResultHeight(const Result *owner) const {
return owner->_height;
}
QString SendData::getResultMime(const Result *owner) const {
return owner->_content_type;
}
QVector<MTPDocumentAttribute> SendData::prepareResultAttributes(const Result *owner) const {
QVector<MTPDocumentAttribute> result;
int duration = owner->_duration;
QSize dimensions(owner->_width, owner->_height);
using Type = Result::Type;
if (owner->_type == Type::Gif) {
const char *filename = (owner->_content_type == qstr("video/mp4") ? "animation.gif.mp4" : "animation.gif");
result.push_back(MTP_documentAttributeFilename(MTP_string(filename)));
result.push_back(MTP_documentAttributeAnimated());
result.push_back(MTP_documentAttributeVideo(MTP_int(owner->_duration), MTP_int(owner->_width), MTP_int(owner->_height)));
} else if (owner->_type == Type::Video) {
result.push_back(MTP_documentAttributeVideo(MTP_int(owner->_duration), MTP_int(owner->_width), MTP_int(owner->_height)));
}
return result;
}
void SendData::setResultDocument(const Result *owner, const MTPDocument &document) const {
owner->_mtpDocument = document;
}
void SendData::setResultPhoto(const Result *owner, const MTPPhoto &photo) const {
owner->_mtpPhoto = photo;
}
MTPDocument SendData::getResultDocument(const Result *owner) const {
return owner->_mtpDocument;
}
MTPPhoto SendData::getResultPhoto(const Result *owner) const {
return owner->_mtpPhoto;
}
SendData::SentMTPMessageFields SendText::getSentMessageFields(const Result*) const {
@ -61,13 +109,14 @@ SendData::SentMTPMessageFields SendContact::getSentMessageFields(const Result*)
}
QString SendContact::getLayoutDescription(const Result *owner) const {
return App::formatPhone(_phoneNumber) + '\n' + owner->description;
return App::formatPhone(_phoneNumber) + '\n' + SendData::getLayoutDescription(owner);
}
SendData::SentMTPMessageFields SendPhoto::getSentMessageFields(const Result *owner) const {
SentMTPMessageFields result;
QImage fileThumb(owner->thumb->pix().toImage());
ImagePtr resultThumb = getResultThumb(owner);
QImage fileThumb(resultThumb->pix().toImage());
QVector<MTPPhotoSize> photoSizes;
@ -75,13 +124,13 @@ SendData::SentMTPMessageFields SendPhoto::getSentMessageFields(const Result *own
ImagePtr thumbPtr = ImagePtr(thumb, "JPG");
photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
QSize medium = resizeKeepAspect(owner->width, owner->height, 320, 320);
QSize medium = resizeKeepAspect(getResultWidth(owner), getResultHeight(owner), 320, 320);
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(owner->width), MTP_int(owner->height), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(getResultWidth(owner)), MTP_int(getResultHeight(owner)), MTP_int(0)));
uint64 photoId = rand_value<uint64>();
PhotoData *ph = App::photoSet(photoId, 0, 0, unixtime(), thumbPtr, ImagePtr(medium.width(), medium.height()), ImagePtr(owner->width, owner->height));
PhotoData *ph = App::photoSet(photoId, 0, 0, unixtime(), thumbPtr, ImagePtr(medium.width(), medium.height()), ImagePtr(getResultWidth(owner), getResultHeight(owner)));
MTPPhoto photo = MTP_photo(MTP_long(photoId), MTP_long(0), MTP_int(ph->date), MTP_vector<MTPPhotoSize>(photoSizes));
result.media = MTP_messageMediaPhoto(photo, MTP_string(_caption));
@ -89,13 +138,14 @@ SendData::SentMTPMessageFields SendPhoto::getSentMessageFields(const Result *own
return result;
}
SendData::SentMTPMessageFields SendFile::getSentMessageFields(const Result *owner) const {
SentMTPMessageFields result;
void SendFile::prepareDocument(const Result *owner) const {
if (getResultDocument(owner).type() != mtpc_documentEmpty) return;
ImagePtr resultThumb = getResultThumb(owner);
MTPPhotoSize thumbSize;
QPixmap thumb;
int32 tw = owner->thumb->width(), th = owner->thumb->height();
if (tw > 0 && th > 0 && tw < 20 * th && th < 20 * tw && owner->thumb->loaded()) {
int32 tw = resultThumb->width(), th = resultThumb->height();
if (tw > 0 && th > 0 && tw < 20 * th && th < 20 * tw && resultThumb->loaded()) {
if (tw > th) {
if (tw > 90) {
th = th * 90 / tw;
@ -106,41 +156,34 @@ SendData::SentMTPMessageFields SendFile::getSentMessageFields(const Result *owne
th = 90;
}
thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(tw), MTP_int(th), MTP_int(0));
thumb = owner->thumb->pixNoCache(tw, th, ImagePixSmooth);
thumb = resultThumb->pixNoCache(tw, th, ImagePixSmooth);
} else {
tw = th = 0;
thumbSize = MTP_photoSizeEmpty(MTP_string(""));
}
uint64 docId = rand_value<uint64>();
QVector<MTPDocumentAttribute> attributes;
int duration = getSentDuration(owner);
QSize dimensions = getSentDimensions(owner);
using Type = Result::Type;
if (owner->type == Type::Gif) {
attributes.push_back(MTP_documentAttributeFilename(MTP_string((owner->content_type == qstr("video/mp4") ? "animation.gif.mp4" : "animation.gif"))));
attributes.push_back(MTP_documentAttributeAnimated());
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(dimensions.width()), MTP_int(dimensions.height())));
} else if (owner->type == Type::Video) {
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(dimensions.width()), MTP_int(dimensions.height())));
}
MTPDocument document = MTP_document(MTP_long(docId), MTP_long(0), MTP_int(unixtime()), MTP_string(owner->content_type), MTP_int(owner->data().size()), thumbSize, MTP_int(MTP::maindc()), MTP_vector<MTPDocumentAttribute>(attributes));
QVector<MTPDocumentAttribute> attributes = prepareResultAttributes(owner);
MTPDocument document = MTP_document(MTP_long(docId), MTP_long(0), MTP_int(unixtime()), MTP_string(getResultMime(owner)), MTP_int(owner->data().size()), thumbSize, MTP_int(MTP::maindc()), MTP_vector<MTPDocumentAttribute>(attributes));
if (tw > 0 && th > 0) {
App::feedDocument(document, thumb);
}
Local::writeStickerImage(mediaKey(DocumentFileLocation, MTP::maindc(), docId), owner->data());
if (!owner->data().isEmpty()) {
Local::writeStickerImage(mediaKey(DocumentFileLocation, MTP::maindc(), docId), owner->data());
}
setResultDocument(owner, document);
}
SendData::SentMTPMessageFields SendFile::getSentMessageFields(const Result *owner) const {
SentMTPMessageFields result;
prepareDocument(owner);
MTPDocument document = getResultDocument(owner);
result.media = MTP_messageMediaDocument(document, MTP_string(_caption));
return result;
}
int SendFile::getSentDuration(const Result *owner) const {
return (_document && _document->duration()) ? _document->duration() : owner->duration;
}
QSize SendFile::getSentDimensions(const Result *owner) const {
return (!_document || _document->dimensions.isEmpty()) ? QSize(owner->width, owner->height) : _document->dimensions;
}
} // namespace internal
} // namespace InlineBots

View file

@ -67,6 +67,20 @@ public:
virtual QString getLayoutTitle(const Result *owner) const;
virtual QString getLayoutDescription(const Result *owner) const;
protected:
ImagePtr getResultThumb(const Result *owner) const;
int getResultWidth(const Result *owner) const;
int getResultHeight(const Result *owner) const;
QString getResultMime(const Result *owner) const;
QVector<MTPDocumentAttribute> prepareResultAttributes(const Result *owner) const;
void setResultDocument(const Result *owner, const MTPDocument &document) const;
void setResultPhoto(const Result *owner, const MTPPhoto &photo) const;
MTPDocument getResultDocument(const Result *owner) const;
MTPPhoto getResultPhoto(const Result *owner) const;
};
// Plain text message.
@ -221,12 +235,11 @@ public:
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
private:
void prepareDocument(const Result *owner) const;
DocumentData *_document;
QString _url, _caption;
int getSentDuration(const Result *owner) const;
QSize getSentDimensions(const Result *owner) const;
};
} // namespace internal