inline_bot_layout_item.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "inline_bots/inline_bot_layout_item.h"
  8. #include "base/never_freed_pointer.h"
  9. #include "data/data_photo.h"
  10. #include "data/data_document.h"
  11. #include "data/data_peer.h"
  12. #include "data/data_file_origin.h"
  13. #include "data/data_document_media.h"
  14. #include "core/click_handler_types.h"
  15. #include "inline_bots/inline_bot_result.h"
  16. #include "inline_bots/inline_bot_layout_internal.h"
  17. #include "storage/localstorage.h"
  18. #include "mainwidget.h"
  19. #include "ui/image/image.h"
  20. #include "ui/empty_userpic.h"
  21. namespace InlineBots {
  22. namespace Layout {
  23. namespace {
  24. base::NeverFreedPointer<DocumentItems> documentItemsMap;
  25. } // namespace
  26. std::shared_ptr<Result> ItemBase::getResult() const {
  27. return _result;
  28. }
  29. DocumentData *ItemBase::getDocument() const {
  30. return _document;
  31. }
  32. PhotoData *ItemBase::getPhoto() const {
  33. return _photo;
  34. }
  35. DocumentData *ItemBase::getPreviewDocument() const {
  36. if (_document) {
  37. return _document;
  38. } else if (_result) {
  39. return _result->_document;
  40. }
  41. return nullptr;
  42. }
  43. PhotoData *ItemBase::getPreviewPhoto() const {
  44. if (_photo) {
  45. return _photo;
  46. } else if (_result) {
  47. return _result->_photo;
  48. }
  49. return nullptr;
  50. }
  51. void ItemBase::preload() const {
  52. const auto origin = fileOrigin();
  53. if (_result) {
  54. if (const auto photo = _result->_photo) {
  55. if (photo->hasExact(Data::PhotoSize::Thumbnail)) {
  56. photo->load(Data::PhotoSize::Thumbnail, origin);
  57. }
  58. } else if (const auto document = _result->_document) {
  59. document->loadThumbnail(origin);
  60. } else if (auto &thumb = _result->_thumbnail; !thumb.empty()) {
  61. thumb.load(_result->_session, origin);
  62. }
  63. } else if (_document) {
  64. _document->loadThumbnail(origin);
  65. } else if (_photo && _photo->hasExact(Data::PhotoSize::Thumbnail)) {
  66. _photo->load(Data::PhotoSize::Thumbnail, origin);
  67. }
  68. }
  69. void ItemBase::update() const {
  70. if (_position >= 0) {
  71. context()->inlineItemRepaint(this);
  72. }
  73. }
  74. void ItemBase::layoutChanged() {
  75. if (_position >= 0) {
  76. context()->inlineItemLayoutChanged(this);
  77. }
  78. }
  79. std::unique_ptr<ItemBase> ItemBase::createLayout(
  80. not_null<Context*> context,
  81. std::shared_ptr<Result> result,
  82. bool forceThumb) {
  83. using Type = Result::Type;
  84. switch (result->_type) {
  85. case Type::Photo:
  86. return std::make_unique<internal::Photo>(context, std::move(result));
  87. case Type::Audio:
  88. case Type::File:
  89. return std::make_unique<internal::File>(context, std::move(result));
  90. case Type::Video:
  91. return std::make_unique<internal::Video>(context, std::move(result));
  92. case Type::Sticker:
  93. return std::make_unique<internal::Sticker>(
  94. context,
  95. std::move(result));
  96. case Type::Gif:
  97. return std::make_unique<internal::Gif>(context, std::move(result));
  98. case Type::Article:
  99. case Type::Geo:
  100. case Type::Venue:
  101. return std::make_unique<internal::Article>(
  102. context,
  103. std::move(result),
  104. forceThumb);
  105. case Type::Game:
  106. return std::make_unique<internal::Game>(context, std::move(result));
  107. case Type::Contact:
  108. return std::make_unique<internal::Contact>(
  109. context,
  110. std::move(result));
  111. }
  112. return nullptr;
  113. }
  114. std::unique_ptr<ItemBase> ItemBase::createLayoutGif(
  115. not_null<Context*> context,
  116. not_null<DocumentData*> document) {
  117. return std::make_unique<internal::Gif>(context, document, true);
  118. }
  119. DocumentData *ItemBase::getResultDocument() const {
  120. return _result ? _result->_document : nullptr;
  121. }
  122. PhotoData *ItemBase::getResultPhoto() const {
  123. return _result ? _result->_photo : nullptr;
  124. }
  125. bool ItemBase::hasResultThumb() const {
  126. return _result
  127. && (!_result->_thumbnail.empty()
  128. || !_result->_locationThumbnail.empty());
  129. }
  130. QImage *ItemBase::getResultThumb(Data::FileOrigin origin) const {
  131. if (_result && !_thumbnail) {
  132. if (!_result->_thumbnail.empty()) {
  133. _thumbnail = _result->_thumbnail.createView();
  134. _result->_thumbnail.load(_result->_session, origin);
  135. } else if (!_result->_locationThumbnail.empty()) {
  136. _thumbnail = _result->_locationThumbnail.createView();
  137. _result->_locationThumbnail.load(_result->_session, origin);
  138. }
  139. }
  140. return (_thumbnail && !_thumbnail->isNull())
  141. ? _thumbnail.get()
  142. : nullptr;
  143. }
  144. QPixmap ItemBase::getResultContactAvatar(int width, int height) const {
  145. if (_result->_type == Result::Type::Contact) {
  146. auto result = Ui::EmptyUserpic(
  147. Ui::EmptyUserpic::UserpicColor(Ui::EmptyUserpic::ColorIndex(
  148. BareId(qHash(_result->_id)))),
  149. _result->getLayoutTitle()
  150. ).generate(width);
  151. if (result.height() != height * style::DevicePixelRatio()) {
  152. result = result.scaled(
  153. QSize(width, height) * style::DevicePixelRatio(),
  154. Qt::IgnoreAspectRatio,
  155. Qt::SmoothTransformation);
  156. }
  157. return result;
  158. }
  159. return QPixmap();
  160. }
  161. int ItemBase::getResultDuration() const {
  162. return 0;
  163. }
  164. QString ItemBase::getResultUrl() const {
  165. return _result->_url;
  166. }
  167. ClickHandlerPtr ItemBase::getResultUrlHandler() const {
  168. if (!_result->_url.isEmpty()) {
  169. return std::make_shared<UrlClickHandler>(_result->_url);
  170. }
  171. return ClickHandlerPtr();
  172. }
  173. ClickHandlerPtr ItemBase::getResultPreviewHandler() const {
  174. if (!_result->_content_url.isEmpty()) {
  175. return std::make_shared<UrlClickHandler>(
  176. _result->_content_url,
  177. false);
  178. } else if (const auto document = _result->_document
  179. ; document && document->createMediaView()->canBePlayed(nullptr)) {
  180. return std::make_shared<OpenFileClickHandler>();
  181. } else if (_result->_photo) {
  182. return std::make_shared<OpenFileClickHandler>();
  183. }
  184. return ClickHandlerPtr();
  185. }
  186. QString ItemBase::getResultThumbLetter() const {
  187. auto parts = QStringView(_result->_url).split('/');
  188. if (!parts.isEmpty()) {
  189. auto domain = parts.at(0);
  190. if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others
  191. domain = parts.at(2);
  192. }
  193. parts = domain.split('@').constLast().split('.');
  194. if (parts.size() > 1) {
  195. return parts.at(parts.size() - 2).at(0).toUpper();
  196. }
  197. }
  198. if (!_result->_title.isEmpty()) {
  199. return _result->_title.at(0).toUpper();
  200. }
  201. return QString();
  202. }
  203. Data::FileOrigin ItemBase::fileOrigin() const {
  204. return _context->inlineItemFileOrigin();
  205. }
  206. const DocumentItems *documentItems() {
  207. return documentItemsMap.data();
  208. }
  209. namespace internal {
  210. void regDocumentItem(
  211. not_null<const DocumentData*> document,
  212. not_null<ItemBase*> item) {
  213. documentItemsMap.createIfNull();
  214. (*documentItemsMap)[document].insert(item);
  215. }
  216. void unregDocumentItem(
  217. not_null<const DocumentData*> document,
  218. not_null<ItemBase*> item) {
  219. if (documentItemsMap) {
  220. auto i = documentItemsMap->find(document);
  221. if (i != documentItemsMap->cend()) {
  222. if (i->second.remove(item) && i->second.empty()) {
  223. documentItemsMap->erase(i);
  224. }
  225. }
  226. if (documentItemsMap->empty()) {
  227. documentItemsMap.clear();
  228. }
  229. }
  230. }
  231. } // namespace internal
  232. } // namespace Layout
  233. } // namespace InlineBots