mirror of https://github.com/procxx/kepka.git
				
				
				
			Inline switch to pm and back support added for inline bots.
This commit is contained in:
		
							parent
							
								
									8e89486fbc
								
							
						
					
					
						commit
						fa0c700ca6
					
				| 
						 | 
				
			
			@ -815,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
			
		|||
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
 | 
			
		||||
"lng_forwarding_from" = "{user} and {count:_not_used_|# other|# others}";
 | 
			
		||||
"lng_forwarding_from_two" = "{user} and {second_user}";
 | 
			
		||||
"lng_inline_switch_choose" = "Choose conversation...";
 | 
			
		||||
"lng_inline_switch_cant" = "Sorry, no way to write here :(";
 | 
			
		||||
 | 
			
		||||
"lng_share_cant" = "Sorry, no way to share here :(";
 | 
			
		||||
"lng_reply_cant" = "Sorry, no way to reply to an old message in supergroup :(";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1090,9 +1090,9 @@ msgBotKbFont: semiboldFont;
 | 
			
		|||
msgBotKbOverOpacity: 0.1;
 | 
			
		||||
msgBotKbIconPadding: 2px;
 | 
			
		||||
msgBotKbUrlIcon: sprite(188px, 338px, 10px, 10px);
 | 
			
		||||
msgBotKbCallbackIcon: msgBotKbUrlIcon;
 | 
			
		||||
msgBotKbRequestPhoneIcon: msgBotKbUrlIcon;
 | 
			
		||||
msgBotKbRequestLocationIcon: msgBotKbUrlIcon;
 | 
			
		||||
//msgBotKbRequestPhoneIcon: msgBotKbUrlIcon;
 | 
			
		||||
//msgBotKbRequestLocationIcon: msgBotKbUrlIcon;
 | 
			
		||||
msgBotKbSwitchPmIcon: sprite(188px, 348px, 10px, 10px);
 | 
			
		||||
msgBotKbButton: botKeyboardButton {
 | 
			
		||||
	margin: 5px;
 | 
			
		||||
	padding: 10px;
 | 
			
		||||
| 
						 | 
				
			
			@ -2124,19 +2124,24 @@ botKbButton: botKeyboardButton {
 | 
			
		|||
	padding: 10px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
	textTop: 9px;
 | 
			
		||||
	downTextTop: 10px;
 | 
			
		||||
	downTextTop: 9px;
 | 
			
		||||
}
 | 
			
		||||
botKbTinyButton: botKeyboardButton {
 | 
			
		||||
	margin: 4px;
 | 
			
		||||
	padding: 3px;
 | 
			
		||||
	height: 25px;
 | 
			
		||||
	textTop: 2px;
 | 
			
		||||
	downTextTop: 3px;
 | 
			
		||||
	downTextTop: 2px;
 | 
			
		||||
}
 | 
			
		||||
botKbScroll: flatScroll(solidScroll) {
 | 
			
		||||
	deltax: 3px;
 | 
			
		||||
	width: 10px;
 | 
			
		||||
}
 | 
			
		||||
switchPmButton: BoxButton(defaultBoxButton) {
 | 
			
		||||
	width: 320px;
 | 
			
		||||
	height: 34px;
 | 
			
		||||
	textTop: 7px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
minPhotoSize: 100px;
 | 
			
		||||
maxMediaSize: 420px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1194,6 +1194,13 @@ void EmojiPanInner::step_selected(uint64 ms, bool timer) {
 | 
			
		|||
	if (_animations.isEmpty()) _a_selected.stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InlineCacheEntry::clearResults() {
 | 
			
		||||
	for_const (const InlineBots::Result *result, results) {
 | 
			
		||||
		delete result;
 | 
			
		||||
	}
 | 
			
		||||
	results.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
 | 
			
		||||
	clearSelection(true);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1259,6 +1266,9 @@ int32 StickerPanInner::countHeight(bool plain) {
 | 
			
		|||
	int result = 0, minLastH = plain ? 0 : (_maxHeight - st::stickerPanPadding);
 | 
			
		||||
	if (_showingInlineItems) {
 | 
			
		||||
		result = st::emojiPanHeader;
 | 
			
		||||
		if (_switchPmButton) {
 | 
			
		||||
			result += _switchPmButton->height() + st::inlineResultsSkip;
 | 
			
		||||
		}
 | 
			
		||||
		for (int i = 0, l = _inlineRows.count(); i < l; ++i) {
 | 
			
		||||
			result += _inlineRows.at(i).height;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1320,19 +1330,23 @@ void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) {
 | 
			
		|||
	}
 | 
			
		||||
	InlineBots::Layout::PaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false);
 | 
			
		||||
 | 
			
		||||
	int32 top = st::emojiPanHeader;
 | 
			
		||||
	int32 fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width());
 | 
			
		||||
	for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) {
 | 
			
		||||
	int top = st::emojiPanHeader;
 | 
			
		||||
	if (_switchPmButton) {
 | 
			
		||||
		top += _switchPmButton->height() + st::inlineResultsSkip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width());
 | 
			
		||||
	for (int row = 0, rows = _inlineRows.size(); row < rows; ++row) {
 | 
			
		||||
		const InlineRow &inlineRow(_inlineRows.at(row));
 | 
			
		||||
		if (top >= r.top() + r.height()) break;
 | 
			
		||||
		if (top + inlineRow.height > r.top()) {
 | 
			
		||||
			int32 left = st::inlineResultsLeft;
 | 
			
		||||
			int left = st::inlineResultsLeft;
 | 
			
		||||
			if (row == rows - 1) context.lastRow = true;
 | 
			
		||||
			for (int32 col = 0, cols = inlineRow.items.size(); col < cols; ++col) {
 | 
			
		||||
			for (int col = 0, cols = inlineRow.items.size(); col < cols; ++col) {
 | 
			
		||||
				if (left >= tox) break;
 | 
			
		||||
 | 
			
		||||
				const InlineItem *item = inlineRow.items.at(col);
 | 
			
		||||
				int32 w = item->width();
 | 
			
		||||
				int w = item->width();
 | 
			
		||||
				if (left + w > fromx) {
 | 
			
		||||
					p.translate(left, top);
 | 
			
		||||
					item->paint(p, r.translated(-left, -top), 0, &context);
 | 
			
		||||
| 
						 | 
				
			
			@ -1713,7 +1727,7 @@ void StickerPanInner::refreshSavedGifs() {
 | 
			
		|||
 | 
			
		||||
void StickerPanInner::inlineBotChanged() {
 | 
			
		||||
	_setGifCommand = false;
 | 
			
		||||
	refreshInlineRows(0, InlineResults(), true);
 | 
			
		||||
	refreshInlineRows(nullptr, nullptr, true);
 | 
			
		||||
	deleteUnusedInlineLayouts();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1897,9 +1911,27 @@ void StickerPanInner::clearInlineRowsPanel() {
 | 
			
		|||
	clearInlineRows(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted) {
 | 
			
		||||
void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) {
 | 
			
		||||
	if (!entry || entry->switchPmText.isEmpty()) {
 | 
			
		||||
		_switchPmButton.reset();
 | 
			
		||||
		_switchPmStartToken.clear();
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!_switchPmButton) {
 | 
			
		||||
			_switchPmButton = MakeUnique<BoxButton>(this, QString(), st::switchPmButton);
 | 
			
		||||
			_switchPmButton->show();
 | 
			
		||||
			_switchPmButton->move(st::inlineResultsLeft, st::emojiPanHeader);
 | 
			
		||||
			connect(_switchPmButton.data(), SIGNAL(clicked()), this, SLOT(onSwitchPm()));
 | 
			
		||||
		}
 | 
			
		||||
		_switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper()
 | 
			
		||||
		_switchPmStartToken = entry->switchPmStartToken;
 | 
			
		||||
	}
 | 
			
		||||
	update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StickerPanInner::refreshInlineRows(UserData *bot, const InlineCacheEntry *entry, bool resultsDeleted) {
 | 
			
		||||
	_inlineBot = bot;
 | 
			
		||||
	if (results.isEmpty() && (!_inlineBot || _inlineBot->username != cInlineGifBotUsername())) {
 | 
			
		||||
	refreshSwitchPmButton(entry);
 | 
			
		||||
	if (!entry || entry->results.isEmpty() && (!_inlineBot || _inlineBot->username != cInlineGifBotUsername())) {
 | 
			
		||||
		if (resultsDeleted) {
 | 
			
		||||
			clearInlineRows(true);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1916,7 +1948,7 @@ int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &res
 | 
			
		|||
	_showingSavedGifs = false;
 | 
			
		||||
	_settings.hide();
 | 
			
		||||
 | 
			
		||||
	int32 count = results.size(), from = validateExistingInlineRows(results), added = 0;
 | 
			
		||||
	int32 count = entry->results.size(), from = validateExistingInlineRows(entry->results), added = 0;
 | 
			
		||||
 | 
			
		||||
	if (count) {
 | 
			
		||||
		_inlineRows.reserve(count);
 | 
			
		||||
| 
						 | 
				
			
			@ -1924,7 +1956,7 @@ int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &res
 | 
			
		|||
		row.items.reserve(InlineItemsMaxPerRow);
 | 
			
		||||
		int32 sumWidth = 0;
 | 
			
		||||
		for (int32 i = from; i < count; ++i) {
 | 
			
		||||
			if (inlineRowsAddItem(0, results.at(i), row, sumWidth)) {
 | 
			
		||||
			if (inlineRowsAddItem(0, entry->results.at(i), row, sumWidth)) {
 | 
			
		||||
				++added;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -2180,14 +2212,18 @@ void StickerPanInner::updateSelected() {
 | 
			
		|||
	QPoint p(mapFromGlobal(_lastMousePos));
 | 
			
		||||
 | 
			
		||||
	if (_showingInlineItems) {
 | 
			
		||||
		int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft, sy = p.y() - st::emojiPanHeader;
 | 
			
		||||
		int32 row = -1, col = -1, sel = -1;
 | 
			
		||||
		int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft;
 | 
			
		||||
		int sy = p.y() - st::emojiPanHeader;
 | 
			
		||||
		if (_switchPmButton) {
 | 
			
		||||
			sy -= _switchPmButton->height() + st::inlineResultsSkip;
 | 
			
		||||
		}
 | 
			
		||||
		int row = -1, col = -1, sel = -1;
 | 
			
		||||
		ClickHandlerPtr lnk;
 | 
			
		||||
		ClickHandlerHost *lnkhost = nullptr;
 | 
			
		||||
		HistoryCursorState cursor = HistoryDefaultCursorState;
 | 
			
		||||
		if (sy >= 0) {
 | 
			
		||||
			row = 0;
 | 
			
		||||
			for (int32 rows = _inlineRows.size(); row < rows; ++row) {
 | 
			
		||||
			for (int rows = _inlineRows.size(); row < rows; ++row) {
 | 
			
		||||
				if (sy < _inlineRows.at(row).height) {
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -2359,6 +2395,13 @@ void StickerPanInner::onUpdateInlineItems() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StickerPanInner::onSwitchPm() {
 | 
			
		||||
	if (_inlineBot && _inlineBot->botInfo) {
 | 
			
		||||
		_inlineBot->botInfo->startToken = _switchPmStartToken;
 | 
			
		||||
		Ui::showPeerHistory(_inlineBot, ShowAndStartBotMsgId);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StickerPanInner::step_selected(uint64 ms, bool timer) {
 | 
			
		||||
	QRegion toUpdate;
 | 
			
		||||
	for (Animations::iterator i = _animations.begin(); i != _animations.end();) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3359,7 +3402,7 @@ bool EmojiPan::ui_isInlineItemBeingChosen() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void EmojiPan::notify_automaticLoadSettingsChangedGif() {
 | 
			
		||||
	for_const (const InlineCacheEntry *entry, _inlineCache) {
 | 
			
		||||
	for_const (const internal::InlineCacheEntry *entry, _inlineCache) {
 | 
			
		||||
		for_const (InlineBots::Result *l, entry->results) {
 | 
			
		||||
			l->automaticLoadSettingsChangedGif();
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -3600,13 +3643,6 @@ bool EmojiPan::hideOnNoInlineResults() {
 | 
			
		|||
	return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmojiPan::InlineCacheEntry::clearResults() {
 | 
			
		||||
	for_const (const InlineBots::Result *result, results) {
 | 
			
		||||
		delete result;
 | 
			
		||||
	}
 | 
			
		||||
	results.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmojiPan::inlineBotChanged() {
 | 
			
		||||
	if (!_inlineBot) return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3643,13 +3679,13 @@ void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
 | 
			
		|||
		uint64 queryId(d.vquery_id.v);
 | 
			
		||||
 | 
			
		||||
		if (!adding) {
 | 
			
		||||
			it = _inlineCache.insert(_inlineQuery, new InlineCacheEntry());
 | 
			
		||||
			it = _inlineCache.insert(_inlineQuery, new internal::InlineCacheEntry());
 | 
			
		||||
		}
 | 
			
		||||
		it.value()->nextOffset = qs(d.vnext_offset);
 | 
			
		||||
		if (d.has_switch_pm() && d.vswitch_pm.type() == mtpc_inlineBotSwitchPM) {
 | 
			
		||||
			const auto &switchPm = d.vswitch_pm.c_inlineBotSwitchPM();
 | 
			
		||||
			it.value()->switchPmText = qs(switchPm.vtext);
 | 
			
		||||
			it.value()->switchPmStartParam = qs(switchPm.vstart_param);
 | 
			
		||||
			it.value()->switchPmStartToken = qs(switchPm.vstart_param);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (int count = v.size()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3657,7 +3693,7 @@ void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
 | 
			
		|||
		}
 | 
			
		||||
		int added = 0;
 | 
			
		||||
		for_const (const auto &res, v) {
 | 
			
		||||
			if (UniquePointer<InlineBots::Result> result = InlineBots::Result::create(queryId, res)) {
 | 
			
		||||
			if (auto result = InlineBots::Result::create(queryId, res)) {
 | 
			
		||||
				++added;
 | 
			
		||||
				it.value()->results.push_back(result.release());
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -3740,16 +3776,18 @@ void EmojiPan::onEmptyInlineRows() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool EmojiPan::refreshInlineRows(int32 *added) {
 | 
			
		||||
	bool clear = true;
 | 
			
		||||
	InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery);
 | 
			
		||||
	auto i = _inlineCache.constFind(_inlineQuery);
 | 
			
		||||
	const internal::InlineCacheEntry *entry = nullptr;
 | 
			
		||||
	if (i != _inlineCache.cend()) {
 | 
			
		||||
		clear = i.value()->results.isEmpty();
 | 
			
		||||
		if (!i.value()->results.isEmpty()) {
 | 
			
		||||
			entry = i.value();
 | 
			
		||||
		}
 | 
			
		||||
		_inlineNextOffset = i.value()->nextOffset;
 | 
			
		||||
	}
 | 
			
		||||
	if (clear) prepareShowHideCache();
 | 
			
		||||
	int32 result = s_inner.refreshInlineRows(_inlineBot, clear ? internal::InlineResults() : i.value()->results, false);
 | 
			
		||||
	if (!entry) prepareShowHideCache();
 | 
			
		||||
	int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false);
 | 
			
		||||
	if (added) *added = result;
 | 
			
		||||
	return !clear;
 | 
			
		||||
	return (entry != nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32 EmojiPan::showInlineRows(bool newResults) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -173,6 +173,16 @@ using InlineResult = InlineBots::Result;
 | 
			
		|||
using InlineResults = QList<InlineBots::Result*>;
 | 
			
		||||
using InlineItem = InlineBots::Layout::ItemBase;
 | 
			
		||||
 | 
			
		||||
struct InlineCacheEntry {
 | 
			
		||||
	~InlineCacheEntry() {
 | 
			
		||||
		clearResults();
 | 
			
		||||
	}
 | 
			
		||||
	QString nextOffset;
 | 
			
		||||
	QString switchPmText, switchPmStartToken;
 | 
			
		||||
	InlineResults results; // owns this results list
 | 
			
		||||
	void clearResults();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EmojiColorPicker : public TWidget {
 | 
			
		||||
	Q_OBJECT
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +368,7 @@ public:
 | 
			
		|||
	void refreshStickers();
 | 
			
		||||
	void refreshRecentStickers(bool resize = true);
 | 
			
		||||
	void refreshSavedGifs();
 | 
			
		||||
	int32 refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted);
 | 
			
		||||
	int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted);
 | 
			
		||||
	void refreshRecent();
 | 
			
		||||
	void inlineBotChanged();
 | 
			
		||||
	void hideInlineRowsPanel();
 | 
			
		||||
| 
						 | 
				
			
			@ -384,12 +394,13 @@ public:
 | 
			
		|||
 | 
			
		||||
	~StickerPanInner();
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
private slots:
 | 
			
		||||
 | 
			
		||||
	void updateSelected();
 | 
			
		||||
	void onSettings();
 | 
			
		||||
	void onPreview();
 | 
			
		||||
	void onUpdateInlineItems();
 | 
			
		||||
	void onSwitchPm();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -416,13 +427,15 @@ private:
 | 
			
		|||
	void paintInlineItems(Painter &p, const QRect &r);
 | 
			
		||||
	void paintStickers(Painter &p, const QRect &r);
 | 
			
		||||
 | 
			
		||||
	int32 _maxHeight;
 | 
			
		||||
	void refreshSwitchPmButton(const InlineCacheEntry *entry);
 | 
			
		||||
 | 
			
		||||
	void appendSet(uint64 setId);
 | 
			
		||||
 | 
			
		||||
	void selectEmoji(EmojiPtr emoji);
 | 
			
		||||
	QRect stickerRect(int tab, int sel);
 | 
			
		||||
 | 
			
		||||
	int32 _maxHeight;
 | 
			
		||||
 | 
			
		||||
	typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
 | 
			
		||||
	Animations _animations;
 | 
			
		||||
	Animation _a_selected;
 | 
			
		||||
| 
						 | 
				
			
			@ -449,6 +462,9 @@ private:
 | 
			
		|||
	QTimer _updateInlineItems;
 | 
			
		||||
	bool _inlineWithThumb;
 | 
			
		||||
 | 
			
		||||
	UniquePointer<BoxButton> _switchPmButton;
 | 
			
		||||
	QString _switchPmStartToken;
 | 
			
		||||
 | 
			
		||||
	typedef QVector<InlineItem*> InlineItems;
 | 
			
		||||
	struct InlineRow {
 | 
			
		||||
		InlineRow() : height(0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -705,16 +721,7 @@ private:
 | 
			
		|||
	QTimer _saveConfigTimer;
 | 
			
		||||
 | 
			
		||||
	// inline bots
 | 
			
		||||
	struct InlineCacheEntry {
 | 
			
		||||
		~InlineCacheEntry() {
 | 
			
		||||
			clearResults();
 | 
			
		||||
		}
 | 
			
		||||
		QString nextOffset;
 | 
			
		||||
		QString switchPmText, switchPmStartParam;
 | 
			
		||||
		internal::InlineResults results;
 | 
			
		||||
		void clearResults();
 | 
			
		||||
	};
 | 
			
		||||
	typedef QMap<QString, InlineCacheEntry*> InlineCache;
 | 
			
		||||
	typedef QMap<QString, internal::InlineCacheEntry*> InlineCache;
 | 
			
		||||
	InlineCache _inlineCache;
 | 
			
		||||
	QTimer _inlineRequestTimer;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,6 +83,14 @@ namespace App {
 | 
			
		|||
			box->connect(box, SIGNAL(confirmed(PeerData*)), App::main(), SLOT(onSharePhoneWithBot(PeerData*)));
 | 
			
		||||
			Ui::showLayer(box);
 | 
			
		||||
		} break;
 | 
			
		||||
 | 
			
		||||
		case HistoryMessageReplyMarkup::Button::SwitchInline: {
 | 
			
		||||
			if (MainWidget *m = App::main()) {
 | 
			
		||||
				if (UserData *bot = msg->history()->peer->asUser()) {
 | 
			
		||||
					m->inlineSwitchLayer('@' + bot->username + ' ' + QString::fromUtf8(button->data));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -266,6 +274,12 @@ namespace Notify {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void switchInlineBotButtonReceived(const QString &query) {
 | 
			
		||||
		if (MainWidget *m = App::main()) {
 | 
			
		||||
			m->notify_switchInlineBotButtonReceived(query);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void migrateUpdated(PeerData *peer) {
 | 
			
		||||
		if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,6 +103,7 @@ namespace Notify {
 | 
			
		|||
	void inlineBotRequesting(bool requesting);
 | 
			
		||||
	void replyMarkupUpdated(const HistoryItem *item);
 | 
			
		||||
	void inlineKeyboardMoved(const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop);
 | 
			
		||||
	void switchInlineBotButtonReceived(const QString &query);
 | 
			
		||||
 | 
			
		||||
	void migrateUpdated(PeerData *peer);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,32 +258,9 @@ void FakeDialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBack
 | 
			
		|||
	history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
History::History(const PeerId &peerId) : width(0), height(0)
 | 
			
		||||
, unreadCount(0)
 | 
			
		||||
, inboxReadBefore(1)
 | 
			
		||||
, outboxReadBefore(1)
 | 
			
		||||
, showFrom(nullptr)
 | 
			
		||||
, unreadBar(nullptr)
 | 
			
		||||
, peer(App::peer(peerId))
 | 
			
		||||
, oldLoaded(false)
 | 
			
		||||
, newLoaded(true)
 | 
			
		||||
, lastMsg(0)
 | 
			
		||||
, msgDraft(0)
 | 
			
		||||
, editDraft(0)
 | 
			
		||||
, showAtMsgId(ShowAtUnreadMsgId)
 | 
			
		||||
, scrollTopItem(nullptr)
 | 
			
		||||
, scrollTopOffset(0)
 | 
			
		||||
, mute(isNotifyMuted(peer->notify))
 | 
			
		||||
, lastKeyboardInited(false)
 | 
			
		||||
, lastKeyboardUsed(false)
 | 
			
		||||
, lastKeyboardId(0)
 | 
			
		||||
, lastKeyboardHiddenId(0)
 | 
			
		||||
, lastKeyboardFrom(0)
 | 
			
		||||
, sendRequestId(0)
 | 
			
		||||
, textCachedFor(0)
 | 
			
		||||
, lastItemTextCache(st::dlgRichMinWidth)
 | 
			
		||||
, typingText(st::dlgRichMinWidth)
 | 
			
		||||
, _sortKeyInChatList(0) {
 | 
			
		||||
History::History(const PeerId &peerId)
 | 
			
		||||
: peer(App::peer(peerId))
 | 
			
		||||
, mute(isNotifyMuted(peer->notify)) {
 | 
			
		||||
	if (peer->isChannel() || (peer->isUser() && peer->asUser()->botInfo)) {
 | 
			
		||||
		outboxReadBefore = INT_MAX;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1191,11 +1168,40 @@ void Histories::remove(const PeerId &peer) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
void checkForSwitchInlineButton(HistoryItem *item) {
 | 
			
		||||
	if (item->out() || !item->hasSwitchInlineButton()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (UserData *user = item->history()->peer->asUser()) {
 | 
			
		||||
		if (!user->botInfo || !user->botInfo->inlineReturnPeerId) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
 | 
			
		||||
			for_const (const auto &row, markup->rows) {
 | 
			
		||||
				for_const (const auto &button, row) {
 | 
			
		||||
					if (button.type == HistoryMessageReplyMarkup::Button::SwitchInline) {
 | 
			
		||||
						Notify::switchInlineBotButtonReceived(QString::fromUtf8(button.data));
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
HistoryItem *Histories::addNewMessage(const MTPMessage &msg, NewMessageType type) {
 | 
			
		||||
	PeerId peer = peerFromMessage(msg);
 | 
			
		||||
	if (!peer) return 0;
 | 
			
		||||
	if (!peer) return nullptr;
 | 
			
		||||
 | 
			
		||||
	return findOrInsert(peer, 0, 0)->addNewMessage(msg, type);
 | 
			
		||||
	HistoryItem *result = findOrInsert(peer, 0, 0)->addNewMessage(msg, type);
 | 
			
		||||
	if (result && type == NewMessageUnread) {
 | 
			
		||||
		checkForSwitchInlineButton(result);
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2614,8 +2620,6 @@ void History::removeBlock(HistoryBlock *block) {
 | 
			
		|||
 | 
			
		||||
History::~History() {
 | 
			
		||||
	clearOnDestroy();
 | 
			
		||||
	deleteAndMark(msgDraft);
 | 
			
		||||
	deleteAndMark(editDraft);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int HistoryBlock::resizeGetHeight(int newWidth, bool resizeAllItems) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3047,6 +3051,7 @@ void HistoryMessageReplyMarkup::createFromButtonRows(const QVector<MTPKeyboardBu
 | 
			
		|||
					case mtpc_keyboardButtonSwitchInline: {
 | 
			
		||||
						const auto &buttonData(button.c_keyboardButtonSwitchInline());
 | 
			
		||||
						buttonRow.push_back({ Button::SwitchInline, qs(buttonData.vtext), qba(buttonData.vquery), 0 });
 | 
			
		||||
						flags |= MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button;
 | 
			
		||||
					} break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -6699,11 +6704,13 @@ void HistoryMessage::KeyboardStyle::paintButtonBg(Painter &p, const QRect &rect,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void HistoryMessage::KeyboardStyle::paintButtonIcon(Painter &p, const QRect &rect, HistoryMessageReplyMarkup::Button::Type type) const {
 | 
			
		||||
	using Button = HistoryMessageReplyMarkup::Button;
 | 
			
		||||
	style::sprite sprite;
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::Url: sprite = st::msgBotKbUrlIcon; break;
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::RequestPhone: sprite = st::msgBotKbRequestPhoneIcon; break;
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::RequestLocation: sprite = st::msgBotKbRequestLocationIcon; break;
 | 
			
		||||
	case Button::Url: sprite = st::msgBotKbUrlIcon; break;
 | 
			
		||||
//	case Button::RequestPhone: sprite = st::msgBotKbRequestPhoneIcon; break;
 | 
			
		||||
//	case Button::RequestLocation: sprite = st::msgBotKbRequestLocationIcon; break;
 | 
			
		||||
	case Button::SwitchInline: sprite = st::msgBotKbSwitchPmIcon; break;
 | 
			
		||||
	}
 | 
			
		||||
	if (!sprite.isEmpty()) {
 | 
			
		||||
		p.drawSprite(rect.x() + rect.width() - sprite.pxWidth() - st::msgBotKbIconPadding, rect.y() + st::msgBotKbIconPadding, sprite);
 | 
			
		||||
| 
						 | 
				
			
			@ -6716,12 +6723,14 @@ void HistoryMessage::KeyboardStyle::paintButtonLoading(Painter &p, const QRect &
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int HistoryMessage::KeyboardStyle::minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const {
 | 
			
		||||
	using Button = HistoryMessageReplyMarkup::Button;
 | 
			
		||||
	int result = 2 * buttonPadding(), iconWidth = 0;
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::Url: iconWidth = st::msgBotKbUrlIcon.pxWidth(); break;
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::RequestPhone: iconWidth = st::msgBotKbRequestPhoneIcon.pxWidth(); break;
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::RequestLocation: iconWidth = st::msgBotKbRequestLocationIcon.pxWidth(); break;
 | 
			
		||||
	case HistoryMessageReplyMarkup::Button::Callback: iconWidth = st::msgInvSendingImg.pxWidth(); break;
 | 
			
		||||
	case Button::Url: iconWidth = st::msgBotKbUrlIcon.pxWidth(); break;
 | 
			
		||||
	//case Button::RequestPhone: iconWidth = st::msgBotKbRequestPhoneIcon.pxWidth(); break;
 | 
			
		||||
	//case Button::RequestLocation: iconWidth = st::msgBotKbRequestLocationIcon.pxWidth(); break;
 | 
			
		||||
	case Button::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.pxWidth(); break;
 | 
			
		||||
	case Button::Callback: iconWidth = st::msgInvSendingImg.pxWidth(); break;
 | 
			
		||||
	}
 | 
			
		||||
	if (iconWidth > 0) {
 | 
			
		||||
		result = std::min(result, iconWidth + 2 * int(st::msgBotKbIconPadding));
 | 
			
		||||
| 
						 | 
				
			
			@ -6855,6 +6864,9 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
 | 
			
		|||
	}
 | 
			
		||||
	if (auto markup = Get<HistoryMessageReplyMarkup>()) {
 | 
			
		||||
		markup->create(*config.markup);
 | 
			
		||||
		if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button) {
 | 
			
		||||
			_flags |= MTPDmessage_ClientFlag::f_has_switch_inline_button;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	initTime();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -351,31 +351,53 @@ public:
 | 
			
		|||
	typedef QList<HistoryBlock*> Blocks;
 | 
			
		||||
	Blocks blocks;
 | 
			
		||||
 | 
			
		||||
	int32 width, height, msgCount, unreadCount;
 | 
			
		||||
	int32 inboxReadBefore, outboxReadBefore;
 | 
			
		||||
	HistoryItem *showFrom;
 | 
			
		||||
	HistoryItem *unreadBar;
 | 
			
		||||
	int width = 0;
 | 
			
		||||
	int height = 0;
 | 
			
		||||
	int32 msgCount = 0;
 | 
			
		||||
	int32 unreadCount = 0;
 | 
			
		||||
	MsgId inboxReadBefore = 1;
 | 
			
		||||
	MsgId outboxReadBefore = 1;
 | 
			
		||||
	HistoryItem *showFrom = nullptr;
 | 
			
		||||
	HistoryItem *unreadBar = nullptr;
 | 
			
		||||
 | 
			
		||||
	PeerData *peer;
 | 
			
		||||
	bool oldLoaded, newLoaded;
 | 
			
		||||
	HistoryItem *lastMsg;
 | 
			
		||||
	bool oldLoaded = false;
 | 
			
		||||
	bool newLoaded = true;
 | 
			
		||||
	HistoryItem *lastMsg = nullptr;
 | 
			
		||||
	QDateTime lastMsgDate;
 | 
			
		||||
 | 
			
		||||
	typedef QList<HistoryItem*> NotifyQueue;
 | 
			
		||||
	NotifyQueue notifies;
 | 
			
		||||
 | 
			
		||||
	HistoryDraft *msgDraft;
 | 
			
		||||
	HistoryEditDraft *editDraft;
 | 
			
		||||
	HistoryDraft *msgDraft() {
 | 
			
		||||
		return _msgDraft.data();
 | 
			
		||||
	}
 | 
			
		||||
	HistoryEditDraft *editDraft() {
 | 
			
		||||
		return _editDraft.data();
 | 
			
		||||
	}
 | 
			
		||||
	void setMsgDraft(UniquePointer<HistoryDraft> &&draft) {
 | 
			
		||||
		_msgDraft = std_::move(draft);
 | 
			
		||||
	}
 | 
			
		||||
	void takeMsgDraft(History *from) {
 | 
			
		||||
		if (auto &draft = from->_msgDraft) {
 | 
			
		||||
			if (!draft->text.isEmpty() && !_msgDraft) {
 | 
			
		||||
				_msgDraft = std_::move(draft);
 | 
			
		||||
				_msgDraft->msgId = 0; // edit and reply to drafts can't migrate
 | 
			
		||||
			}
 | 
			
		||||
			from->clearMsgDraft();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	void setEditDraft(UniquePointer<HistoryEditDraft> &&draft) {
 | 
			
		||||
		_editDraft = std_::move(draft);
 | 
			
		||||
	}
 | 
			
		||||
	void clearMsgDraft() {
 | 
			
		||||
		_msgDraft.clear();
 | 
			
		||||
	}
 | 
			
		||||
	void clearEditDraft() {
 | 
			
		||||
		_editDraft.clear();
 | 
			
		||||
	}
 | 
			
		||||
	HistoryDraft *draft() {
 | 
			
		||||
		return editDraft ? editDraft : msgDraft;
 | 
			
		||||
	}
 | 
			
		||||
	void setMsgDraft(HistoryDraft *draft) {
 | 
			
		||||
		if (msgDraft) delete msgDraft;
 | 
			
		||||
		msgDraft = draft;
 | 
			
		||||
	}
 | 
			
		||||
	void setEditDraft(HistoryEditDraft *draft) {
 | 
			
		||||
		if (editDraft) delete editDraft;
 | 
			
		||||
		editDraft = draft;
 | 
			
		||||
		return _editDraft ? editDraft() : msgDraft();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// some fields below are a property of a currently displayed instance of this
 | 
			
		||||
| 
						 | 
				
			
			@ -383,13 +405,13 @@ public:
 | 
			
		|||
public:
 | 
			
		||||
	// we save the last showAtMsgId to restore the state when switching
 | 
			
		||||
	// between different conversation histories
 | 
			
		||||
	MsgId showAtMsgId;
 | 
			
		||||
	MsgId showAtMsgId = ShowAtUnreadMsgId;
 | 
			
		||||
 | 
			
		||||
	// we save a pointer of the history item at the top of the displayed window
 | 
			
		||||
	// together with an offset from the window top to the top of this message
 | 
			
		||||
	// resulting scrollTop = top(scrollTopItem) + scrollTopOffset
 | 
			
		||||
	HistoryItem *scrollTopItem;
 | 
			
		||||
	int scrollTopOffset;
 | 
			
		||||
	HistoryItem *scrollTopItem = nullptr;
 | 
			
		||||
	int scrollTopOffset = 0;
 | 
			
		||||
	void forgetScrollState() {
 | 
			
		||||
		scrollTopItem = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -412,21 +434,23 @@ public:
 | 
			
		|||
 | 
			
		||||
	bool mute;
 | 
			
		||||
 | 
			
		||||
	bool lastKeyboardInited, lastKeyboardUsed;
 | 
			
		||||
	MsgId lastKeyboardId, lastKeyboardHiddenId;
 | 
			
		||||
	PeerId lastKeyboardFrom;
 | 
			
		||||
	bool lastKeyboardInited = false;
 | 
			
		||||
	bool lastKeyboardUsed = false;
 | 
			
		||||
	MsgId lastKeyboardId = 0;
 | 
			
		||||
	MsgId lastKeyboardHiddenId = 0;
 | 
			
		||||
	PeerId lastKeyboardFrom = 0;
 | 
			
		||||
 | 
			
		||||
	mtpRequestId sendRequestId;
 | 
			
		||||
	mtpRequestId sendRequestId = 0;
 | 
			
		||||
 | 
			
		||||
	mutable const HistoryItem *textCachedFor; // cache
 | 
			
		||||
	mutable Text lastItemTextCache;
 | 
			
		||||
	mutable const HistoryItem *textCachedFor = nullptr; // cache
 | 
			
		||||
	mutable Text lastItemTextCache = Text{ int(st::dlgRichMinWidth) };
 | 
			
		||||
 | 
			
		||||
	typedef QMap<UserData*, uint64> TypingUsers;
 | 
			
		||||
	TypingUsers typing;
 | 
			
		||||
	typedef QMap<UserData*, SendAction> SendActionUsers;
 | 
			
		||||
	SendActionUsers sendActions;
 | 
			
		||||
	QString typingStr;
 | 
			
		||||
	Text typingText;
 | 
			
		||||
	Text typingText = Text{ int(st::dlgRichMinWidth) };
 | 
			
		||||
	uint32 typingDots;
 | 
			
		||||
	QMap<SendActionType, uint64> mySendActions;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -536,7 +560,7 @@ private:
 | 
			
		|||
		t_assert(it != _chatListLinks.cend());
 | 
			
		||||
		return it.value();
 | 
			
		||||
	}
 | 
			
		||||
	uint64 _sortKeyInChatList; // like ((unixtime) << 32) | (incremented counter)
 | 
			
		||||
	uint64 _sortKeyInChatList = 0; // like ((unixtime) << 32) | (incremented counter)
 | 
			
		||||
 | 
			
		||||
	typedef QMap<MsgId, NullType> MediaOverviewIds;
 | 
			
		||||
	MediaOverviewIds overviewIds[OverviewCount];
 | 
			
		||||
| 
						 | 
				
			
			@ -555,6 +579,9 @@ private:
 | 
			
		|||
	// Depending on isBuildingFrontBlock() gets front or back block.
 | 
			
		||||
	HistoryBlock *prepareBlockForAddingItem();
 | 
			
		||||
 | 
			
		||||
	UniquePointer<HistoryDraft> _msgDraft;
 | 
			
		||||
	UniquePointer<HistoryEditDraft> _editDraft;
 | 
			
		||||
 | 
			
		||||
 };
 | 
			
		||||
 | 
			
		||||
class HistoryGroup;
 | 
			
		||||
| 
						 | 
				
			
			@ -1425,6 +1452,9 @@ public:
 | 
			
		|||
		// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
 | 
			
		||||
		return qFlags(MTPDreplyKeyboardMarkup_ClientFlag::f_zero);
 | 
			
		||||
	}
 | 
			
		||||
	bool hasSwitchInlineButton() const {
 | 
			
		||||
		return _flags & MTPDmessage_ClientFlag::f_has_switch_inline_button;
 | 
			
		||||
	}
 | 
			
		||||
	bool hasTextLinks() const {
 | 
			
		||||
		return _flags & MTPDmessage_ClientFlag::f_has_text_links;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2404,6 +2404,23 @@ HistoryHider::HistoryHider(MainWidget *parent) : TWidget(parent)
 | 
			
		|||
	init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HistoryHider::HistoryHider(MainWidget *parent, const QString &botAndQuery) : TWidget(parent)
 | 
			
		||||
, _sharedContact(0)
 | 
			
		||||
, _forwardSelected(false)
 | 
			
		||||
, _sendPath(false)
 | 
			
		||||
, _botAndQuery(botAndQuery)
 | 
			
		||||
, _send(this, lang(lng_forward_send), st::defaultBoxButton)
 | 
			
		||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
 | 
			
		||||
, offered(0)
 | 
			
		||||
, a_opacity(0, 1)
 | 
			
		||||
, _a_appearance(animation(this, &HistoryHider::step_appearance))
 | 
			
		||||
, hiding(false)
 | 
			
		||||
, _forwardRequest(0)
 | 
			
		||||
, toTextWidth(0)
 | 
			
		||||
, shadow(st::boxShadow) {
 | 
			
		||||
	init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString &text) : TWidget(parent)
 | 
			
		||||
, _sharedContact(0)
 | 
			
		||||
, _forwardSelected(false)
 | 
			
		||||
| 
						 | 
				
			
			@ -2427,7 +2444,7 @@ void HistoryHider::init() {
 | 
			
		|||
	connect(&_cancel, SIGNAL(clicked()), this, SLOT(startHide()));
 | 
			
		||||
	connect(App::wnd()->getTitle(), SIGNAL(hiderClicked()), this, SLOT(startHide()));
 | 
			
		||||
 | 
			
		||||
	_chooseWidth = st::forwardFont->width(lang(lng_forward_choose));
 | 
			
		||||
	_chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose));
 | 
			
		||||
 | 
			
		||||
	resizeEvent(0);
 | 
			
		||||
	_a_appearance.start();
 | 
			
		||||
| 
						 | 
				
			
			@ -2474,7 +2491,7 @@ void HistoryHider::paintEvent(QPaintEvent *e) {
 | 
			
		|||
			App::roundRect(p, (width() - w) / 2, (height() - h) / 2, w, h, st::forwardBg, ForwardCorners);
 | 
			
		||||
 | 
			
		||||
			p.setPen(st::white->p);
 | 
			
		||||
			p.drawText(box, lang(lng_forward_choose), QTextOption(style::al_center));
 | 
			
		||||
			p.drawText(box, lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose), QTextOption(style::al_center));
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		p.drawPixmap(box.left(), box.top(), cacheForAnim);
 | 
			
		||||
| 
						 | 
				
			
			@ -2529,6 +2546,8 @@ void HistoryHider::forward() {
 | 
			
		|||
			parent()->onSendPaths(offered->id);
 | 
			
		||||
		} else if (!_shareUrl.isEmpty()) {
 | 
			
		||||
			parent()->onShareUrl(offered->id, _shareUrl, _shareText);
 | 
			
		||||
		} else if (!_botAndQuery.isEmpty()) {
 | 
			
		||||
			parent()->onInlineSwitchChosen(offered->id, _botAndQuery);
 | 
			
		||||
		} else {
 | 
			
		||||
			parent()->onForward(offered->id, _forwardSelected ? ForwardSelectedMessages : ForwardContextMessage);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -2593,6 +2612,13 @@ bool HistoryHider::offerPeer(PeerId peer) {
 | 
			
		|||
			startHide();
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	} else if (!_botAndQuery.isEmpty()) {
 | 
			
		||||
		PeerId to = offered->id;
 | 
			
		||||
		offered = 0;
 | 
			
		||||
		if (parent()->onInlineSwitchChosen(to, _botAndQuery)) {
 | 
			
		||||
			startHide();
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	} else {
 | 
			
		||||
		PeerId to = offered->id;
 | 
			
		||||
		offered = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -2974,7 +3000,8 @@ void HistoryWidget::onDraftSave(bool delayed) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft) {
 | 
			
		||||
	if (!msgDraft && _editMsgId) msgDraft = &_history->msgDraft;
 | 
			
		||||
	HistoryDraft *historyMsgDraft = _history ? _history->msgDraft() : nullptr;
 | 
			
		||||
	if (!msgDraft && _editMsgId) msgDraft = &historyMsgDraft;
 | 
			
		||||
 | 
			
		||||
	bool save = _peer && (_saveDraftStart > 0);
 | 
			
		||||
	_saveDraftStart = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -3029,13 +3056,13 @@ void HistoryWidget::writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **edit
 | 
			
		|||
void HistoryWidget::writeDrafts(History *history) {
 | 
			
		||||
	Local::MessageDraft localMsgDraft, localEditDraft;
 | 
			
		||||
	MessageCursor msgCursor, editCursor;
 | 
			
		||||
	if (history->msgDraft) {
 | 
			
		||||
		localMsgDraft = Local::MessageDraft(history->msgDraft->msgId, history->msgDraft->text, history->msgDraft->previewCancelled);
 | 
			
		||||
		msgCursor = history->msgDraft->cursor;
 | 
			
		||||
	if (auto msgDraft = history->msgDraft()) {
 | 
			
		||||
		localMsgDraft = Local::MessageDraft(msgDraft->msgId, msgDraft->text, msgDraft->previewCancelled);
 | 
			
		||||
		msgCursor = msgDraft->cursor;
 | 
			
		||||
	}
 | 
			
		||||
	if (history->editDraft) {
 | 
			
		||||
		localEditDraft = Local::MessageDraft(history->editDraft->msgId, history->editDraft->text, history->editDraft->previewCancelled);
 | 
			
		||||
		editCursor = history->editDraft->cursor;
 | 
			
		||||
	if (auto editDraft = history->editDraft()) {
 | 
			
		||||
		localEditDraft = Local::MessageDraft(editDraft->msgId, editDraft->text, editDraft->previewCancelled);
 | 
			
		||||
		editCursor = editDraft->cursor;
 | 
			
		||||
	}
 | 
			
		||||
	Local::writeDrafts(history->peer->id, localMsgDraft, localEditDraft);
 | 
			
		||||
	Local::writeDraftCursors(history->peer->id, msgCursor, editCursor);
 | 
			
		||||
| 
						 | 
				
			
			@ -3201,6 +3228,25 @@ void HistoryWidget::notify_inlineKeyboardMoved(const HistoryItem *item, int oldK
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query) {
 | 
			
		||||
	if (!_peer) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (UserData *bot = _peer->asUser()) {
 | 
			
		||||
		if (!bot->botInfo || !bot->botInfo->inlineReturnPeerId) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		History *h = App::history(bot->botInfo->inlineReturnPeerId);
 | 
			
		||||
		auto text = '@' + bot->username + ' ' + query;
 | 
			
		||||
		h->setMsgDraft(MakeUnique<HistoryDraft>(text, 0, MessageCursor(text.size(), text.size(), QFIXED_MAX), false));
 | 
			
		||||
		if (h == _history) {
 | 
			
		||||
			applyDraft();
 | 
			
		||||
		} else {
 | 
			
		||||
			Ui::showPeerHistory(bot->botInfo->inlineReturnPeerId, ShowAtUnreadMsgId);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::notify_userIsBotChanged(UserData *user) {
 | 
			
		||||
	if (_peer && _peer == user) {
 | 
			
		||||
		_list->notifyIsBotChanged();
 | 
			
		||||
| 
						 | 
				
			
			@ -3480,7 +3526,7 @@ void HistoryWidget::fastShowAtEnd(History *h) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::applyDraft(bool parseLinks) {
 | 
			
		||||
	HistoryDraft *draft = _history ? _history->draft() : 0;
 | 
			
		||||
	HistoryDraft *draft = _history ? _history->draft() : nullptr;
 | 
			
		||||
	if (!draft) {
 | 
			
		||||
		setFieldText(QString());
 | 
			
		||||
		_field.setFocus();
 | 
			
		||||
| 
						 | 
				
			
			@ -3494,12 +3540,12 @@ void HistoryWidget::applyDraft(bool parseLinks) {
 | 
			
		|||
	draft->cursor.applyTo(_field);
 | 
			
		||||
	_textUpdateEventsFlags = TextUpdateEventsSaveDraft | TextUpdateEventsSendTyping;
 | 
			
		||||
	_previewCancelled = draft->previewCancelled;
 | 
			
		||||
	if (_history->editDraft) {
 | 
			
		||||
		_editMsgId = _history->editDraft->msgId;
 | 
			
		||||
	if (auto editDraft = _history->editDraft()) {
 | 
			
		||||
		_editMsgId = editDraft->msgId;
 | 
			
		||||
		_replyToId = 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		_editMsgId = 0;
 | 
			
		||||
		_replyToId = readyToForward() ? 0 : _history->msgDraft->msgId;
 | 
			
		||||
		_replyToId = readyToForward() ? 0 : _history->msgDraft()->msgId;
 | 
			
		||||
	}
 | 
			
		||||
	if (parseLinks) {
 | 
			
		||||
		onPreviewParse();
 | 
			
		||||
| 
						 | 
				
			
			@ -3516,6 +3562,11 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 | 
			
		|||
	MsgId wasMsgId = _showAtMsgId;
 | 
			
		||||
	History *wasHistory = _history;
 | 
			
		||||
 | 
			
		||||
	bool startBot = (showAtMsgId == ShowAndStartBotMsgId);
 | 
			
		||||
	if (startBot) {
 | 
			
		||||
		showAtMsgId = ShowAtTheEndMsgId;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (_history) {
 | 
			
		||||
		if (_peer->id == peerId && !reload) {
 | 
			
		||||
			_history->forgetScrollState();
 | 
			
		||||
| 
						 | 
				
			
			@ -3555,6 +3606,13 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 | 
			
		|||
 | 
			
		||||
			App::main()->topBar()->update();
 | 
			
		||||
			update();
 | 
			
		||||
 | 
			
		||||
			if (startBot && _peer->isUser() && _peer->asUser()->botInfo) {
 | 
			
		||||
				if (wasHistory) _peer->asUser()->botInfo->inlineReturnPeerId = wasHistory->peer->id;
 | 
			
		||||
				onBotStart();
 | 
			
		||||
				_history->clearMsgDraft();
 | 
			
		||||
				applyDraft();
 | 
			
		||||
			}
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (_history->mySendActions.contains(SendActionTyping)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3571,21 +3629,23 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 | 
			
		|||
 | 
			
		||||
	if (_history) {
 | 
			
		||||
		if (_editMsgId) {
 | 
			
		||||
			_history->setEditDraft(new HistoryEditDraft(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId));
 | 
			
		||||
			_history->setEditDraft(MakeUnique<HistoryEditDraft>(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId));
 | 
			
		||||
		} else {
 | 
			
		||||
			if (_replyToId || !_field.getLastText().isEmpty()) {
 | 
			
		||||
				_history->setMsgDraft(new HistoryDraft(_field, _replyToId, _previewCancelled));
 | 
			
		||||
				_history->setMsgDraft(MakeUnique<HistoryDraft>(_field, _replyToId, _previewCancelled));
 | 
			
		||||
			} else {
 | 
			
		||||
				_history->setMsgDraft(nullptr);
 | 
			
		||||
				_history->clearMsgDraft();
 | 
			
		||||
			}
 | 
			
		||||
			_history->setEditDraft(nullptr);
 | 
			
		||||
			_history->clearEditDraft();
 | 
			
		||||
		}
 | 
			
		||||
		if (_migrated) {
 | 
			
		||||
			_migrated->setMsgDraft(nullptr); // use migrated draft only once
 | 
			
		||||
			_migrated->setEditDraft(nullptr);
 | 
			
		||||
			_migrated->clearEditDraft(); // use migrated draft only once
 | 
			
		||||
			_migrated->clearEditDraft();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		writeDrafts(&_history->msgDraft, &_history->editDraft);
 | 
			
		||||
		auto msgDraft = _history->msgDraft();
 | 
			
		||||
		auto editDraft = _history->editDraft();
 | 
			
		||||
		writeDrafts(&msgDraft, &editDraft);
 | 
			
		||||
 | 
			
		||||
		_history->showAtMsgId = _showAtMsgId;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3618,7 +3678,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 | 
			
		|||
	_showAtMsgId = showAtMsgId;
 | 
			
		||||
	_histInited = false;
 | 
			
		||||
 | 
			
		||||
	_peer = peerId ? App::peer(peerId) : 0;
 | 
			
		||||
	_peer = peerId ? App::peer(peerId) : nullptr;
 | 
			
		||||
	_channel = _peer ? peerToChannel(_peer->id) : NoChannel;
 | 
			
		||||
	_canSendMessages = canSendMessages(_peer);
 | 
			
		||||
	if (_peer && _peer->isChannel()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3695,14 +3755,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 | 
			
		|||
		Local::readDraftsWithCursors(_history);
 | 
			
		||||
		if (_migrated) {
 | 
			
		||||
			Local::readDraftsWithCursors(_migrated);
 | 
			
		||||
			_migrated->setEditDraft(nullptr);
 | 
			
		||||
			if (_migrated->msgDraft && !_migrated->msgDraft->text.isEmpty()) {
 | 
			
		||||
				_migrated->msgDraft->msgId = 0; // edit and reply to drafts can't migrate
 | 
			
		||||
				if (!_history->msgDraft) {
 | 
			
		||||
					_history->setMsgDraft(new HistoryDraft(*_migrated->msgDraft));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			_migrated->setMsgDraft(nullptr);
 | 
			
		||||
			_migrated->clearEditDraft();
 | 
			
		||||
			_history->takeMsgDraft(_migrated);
 | 
			
		||||
		}
 | 
			
		||||
		applyDraft(false);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3713,6 +3767,11 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 | 
			
		|||
 | 
			
		||||
		connect(&_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged()));
 | 
			
		||||
		connect(&_scroll, SIGNAL(scrolled()), _list, SLOT(onUpdateSelected()));
 | 
			
		||||
 | 
			
		||||
		if (startBot && _peer->isUser() && _peer->asUser()->botInfo) {
 | 
			
		||||
			if (wasHistory) _peer->asUser()->botInfo->inlineReturnPeerId = wasHistory->peer->id;
 | 
			
		||||
			onBotStart();
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		setFieldText(QString());
 | 
			
		||||
		doneShow();
 | 
			
		||||
| 
						 | 
				
			
			@ -4636,19 +4695,23 @@ void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates,
 | 
			
		|||
		_saveEditMsgRequestId = 0;
 | 
			
		||||
		cancelEdit();
 | 
			
		||||
	}
 | 
			
		||||
	if (history->editDraft && history->editDraft->saveRequest == req) {
 | 
			
		||||
		history->setEditDraft(nullptr);
 | 
			
		||||
	if (auto editDraft = history->editDraft()) {
 | 
			
		||||
		if (editDraft->saveRequest == req) {
 | 
			
		||||
			history->clearEditDraft();
 | 
			
		||||
			writeDrafts(history);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtpRequestId req) {
 | 
			
		||||
	if (MTP::isDefaultHandledError(error)) return false;
 | 
			
		||||
	if (req == _saveEditMsgRequestId) {
 | 
			
		||||
		_saveEditMsgRequestId = 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (history->editDraft && history->editDraft->saveRequest == req) {
 | 
			
		||||
		history->editDraft->saveRequest = 0;
 | 
			
		||||
	if (auto editDraft = history->editDraft()) {
 | 
			
		||||
		if (editDraft->saveRequest == req) {
 | 
			
		||||
			editDraft->saveRequest = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	QString err = error.type();
 | 
			
		||||
| 
						 | 
				
			
			@ -7105,10 +7168,10 @@ void HistoryWidget::onReplyToMessage() {
 | 
			
		|||
	App::main()->cancelForwarding();
 | 
			
		||||
 | 
			
		||||
	if (_editMsgId) {
 | 
			
		||||
		if (!_history->msgDraft) {
 | 
			
		||||
			_history->setMsgDraft(new HistoryDraft(QString(), to->id, MessageCursor(), false));
 | 
			
		||||
		if (auto msgDraft = _history->msgDraft()) {
 | 
			
		||||
			msgDraft->msgId = to->id;
 | 
			
		||||
		} else {
 | 
			
		||||
			_history->msgDraft->msgId = to->id;
 | 
			
		||||
			_history->setMsgDraft(MakeUnique<HistoryDraft>(QString(), to->id, MessageCursor(), false));
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		_replyEditMsg = to;
 | 
			
		||||
| 
						 | 
				
			
			@ -7142,13 +7205,13 @@ void HistoryWidget::onEditMessage() {
 | 
			
		|||
		delete box;
 | 
			
		||||
 | 
			
		||||
		if (_replyToId || !_field.getLastText().isEmpty()) {
 | 
			
		||||
			_history->setMsgDraft(new HistoryDraft(_field, _replyToId, _previewCancelled));
 | 
			
		||||
			_history->setMsgDraft(MakeUnique<HistoryDraft>(_field, _replyToId, _previewCancelled));
 | 
			
		||||
		} else {
 | 
			
		||||
			_history->setMsgDraft(nullptr);
 | 
			
		||||
			_history->clearMsgDraft();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		QString text(textApplyEntities(to->originalText(), to->originalEntities()));
 | 
			
		||||
		_history->setEditDraft(new HistoryEditDraft(text, to->id, MessageCursor(text.size(), text.size(), QFIXED_MAX), false));
 | 
			
		||||
		_history->setEditDraft(MakeUnique<HistoryEditDraft>(text, to->id, MessageCursor(text.size(), text.size(), QFIXED_MAX), false));
 | 
			
		||||
		applyDraft(false);
 | 
			
		||||
 | 
			
		||||
		_previewData = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -7249,8 +7312,10 @@ bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::cancelReply(bool lastKeyboardUsed) {
 | 
			
		||||
	bool wasReply = _replyToId || (_history && _history->msgDraft && _history->msgDraft->msgId);
 | 
			
		||||
	bool wasReply = false; ;
 | 
			
		||||
	if (_replyToId) {
 | 
			
		||||
		wasReply = true;
 | 
			
		||||
 | 
			
		||||
		_replyEditMsg = 0;
 | 
			
		||||
		_replyToId = 0;
 | 
			
		||||
		mouseMoveEvent(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -7263,11 +7328,13 @@ void HistoryWidget::cancelReply(bool lastKeyboardUsed) {
 | 
			
		|||
 | 
			
		||||
		resizeEvent(0);
 | 
			
		||||
		update();
 | 
			
		||||
	} else if (wasReply) {
 | 
			
		||||
		if (_history->msgDraft->text.isEmpty()) {
 | 
			
		||||
			_history->setMsgDraft(nullptr);
 | 
			
		||||
	} else if (auto msgDraft = (_history ? _history->msgDraft() : nullptr)) {
 | 
			
		||||
		if (msgDraft->msgId) {
 | 
			
		||||
			if (msgDraft->text.isEmpty()) {
 | 
			
		||||
				_history->clearMsgDraft();
 | 
			
		||||
			} else {
 | 
			
		||||
			_history->msgDraft->msgId = 0;
 | 
			
		||||
				msgDraft->msgId = 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (wasReply) {
 | 
			
		||||
| 
						 | 
				
			
			@ -7287,7 +7354,7 @@ void HistoryWidget::cancelEdit() {
 | 
			
		|||
 | 
			
		||||
	_editMsgId = 0;
 | 
			
		||||
	_replyEditMsg = 0;
 | 
			
		||||
	_history->setEditDraft(nullptr);
 | 
			
		||||
	_history->clearEditDraft();
 | 
			
		||||
	applyDraft();
 | 
			
		||||
 | 
			
		||||
	if (_saveEditMsgRequestId) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -398,6 +398,7 @@ public:
 | 
			
		|||
	HistoryHider(MainWidget *parent, UserData *sharedContact); // share contact
 | 
			
		||||
	HistoryHider(MainWidget *parent); // send path from command line argument
 | 
			
		||||
	HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url
 | 
			
		||||
	HistoryHider(MainWidget *parent, const QString &botAndQuery); // inline switch button handler
 | 
			
		||||
 | 
			
		||||
	void step_appearance(float64 ms, bool timer);
 | 
			
		||||
	bool withConfirm() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -409,6 +410,9 @@ public:
 | 
			
		|||
 | 
			
		||||
	bool offerPeer(PeerId peer);
 | 
			
		||||
	QString offeredText() const;
 | 
			
		||||
	QString botAndQuery() const {
 | 
			
		||||
		return _botAndQuery;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool wasOffered() const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -434,6 +438,7 @@ private:
 | 
			
		|||
	bool _forwardSelected, _sendPath;
 | 
			
		||||
 | 
			
		||||
	QString _shareUrl, _shareText;
 | 
			
		||||
	QString _botAndQuery;
 | 
			
		||||
 | 
			
		||||
	BoxButton _send, _cancel;
 | 
			
		||||
	PeerData *offered;
 | 
			
		||||
| 
						 | 
				
			
			@ -685,6 +690,7 @@ public:
 | 
			
		|||
	void notify_inlineBotRequesting(bool requesting);
 | 
			
		||||
	void notify_replyMarkupUpdated(const HistoryItem *item);
 | 
			
		||||
	void notify_inlineKeyboardMoved(const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop);
 | 
			
		||||
	void notify_switchInlineBotButtonReceived(const QString &query);
 | 
			
		||||
	void notify_userIsBotChanged(UserData *user);
 | 
			
		||||
	void notify_migrateUpdated(PeerData *peer);
 | 
			
		||||
	void notify_clipStopperHidden(ClipStopperType type);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2441,14 +2441,14 @@ namespace Local {
 | 
			
		|||
		_readDraftCursors(peer, msgCursor, editCursor);
 | 
			
		||||
 | 
			
		||||
		if (msgText.isEmpty() && !msgReplyTo) {
 | 
			
		||||
			h->setMsgDraft(nullptr);
 | 
			
		||||
			h->clearMsgDraft();
 | 
			
		||||
		} else {
 | 
			
		||||
			h->setMsgDraft(new HistoryDraft(msgText, msgReplyTo, msgCursor, msgPreviewCancelled));
 | 
			
		||||
			h->setMsgDraft(MakeUnique<HistoryDraft>(msgText, msgReplyTo, msgCursor, msgPreviewCancelled));
 | 
			
		||||
		}
 | 
			
		||||
		if (!editMsgId) {
 | 
			
		||||
			h->setEditDraft(nullptr);
 | 
			
		||||
			h->clearEditDraft();
 | 
			
		||||
		} else {
 | 
			
		||||
			h->setEditDraft(new HistoryEditDraft(editText, editMsgId, editCursor, editPreviewCancelled));
 | 
			
		||||
			h->setEditDraft(MakeUnique<HistoryEditDraft>(editText, editMsgId, editCursor, editPreviewCancelled));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -508,8 +508,26 @@ bool MainWidget::onShareUrl(const PeerId &peer, const QString &url, const QStrin
 | 
			
		|||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	History *h = App::history(peer);
 | 
			
		||||
	h->setMsgDraft(new HistoryDraft(url + '\n' + text, 0, MessageCursor(url.size() + 1, url.size() + 1 + text.size(), QFIXED_MAX), false));
 | 
			
		||||
	h->setEditDraft(nullptr);
 | 
			
		||||
	h->setMsgDraft(MakeUnique<HistoryDraft>(url + '\n' + text, 0, MessageCursor(url.size() + 1, url.size() + 1 + text.size(), QFIXED_MAX), false));
 | 
			
		||||
	h->clearEditDraft();
 | 
			
		||||
	bool opened = history.peer() && (history.peer()->id == peer);
 | 
			
		||||
	if (opened) {
 | 
			
		||||
		history.applyDraft();
 | 
			
		||||
	} else {
 | 
			
		||||
		Ui::showPeerHistory(peer, ShowAtUnreadMsgId);
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MainWidget::onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery) {
 | 
			
		||||
	PeerData *p = App::peer(peer);
 | 
			
		||||
	if (!peer || (p->isChannel() && !p->asChannel()->canPublish() && p->asChannel()->isBroadcast()) || (p->isChat() && !p->asChat()->canWrite()) || (p->isUser() && p->asUser()->access == UserNoAccess)) {
 | 
			
		||||
		Ui::showLayer(new InformBox(lang(lng_inline_switch_cant)));
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	History *h = App::history(peer);
 | 
			
		||||
	h->setMsgDraft(MakeUnique<HistoryDraft>(botAndQuery, 0, MessageCursor(botAndQuery.size(), botAndQuery.size(), QFIXED_MAX), false));
 | 
			
		||||
	h->clearEditDraft();
 | 
			
		||||
	bool opened = history.peer() && (history.peer()->id == peer);
 | 
			
		||||
	if (opened) {
 | 
			
		||||
		history.applyDraft();
 | 
			
		||||
| 
						 | 
				
			
			@ -752,6 +770,10 @@ void MainWidget::notify_inlineKeyboardMoved(const HistoryItem *item, int oldKeyb
 | 
			
		|||
	history.notify_inlineKeyboardMoved(item, oldKeyboardTop, newKeyboardTop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainWidget::notify_switchInlineBotButtonReceived(const QString &query) {
 | 
			
		||||
	history.notify_switchInlineBotButtonReceived(query);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainWidget::notify_userIsBotChanged(UserData *bot) {
 | 
			
		||||
	history.notify_userIsBotChanged(bot);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -931,10 +953,18 @@ void MainWidget::shareUrlLayer(const QString &url, const QString &text) {
 | 
			
		|||
	hiderLayer(new HistoryHider(this, url, text));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainWidget::inlineSwitchLayer(const QString &botAndQuery) {
 | 
			
		||||
	hiderLayer(new HistoryHider(this, botAndQuery));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MainWidget::selectingPeer(bool withConfirm) {
 | 
			
		||||
	return _hider ? (withConfirm ? _hider->withConfirm() : true) : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MainWidget::selectingPeerForInlineSwitch() {
 | 
			
		||||
	return selectingPeer() ? !_hider->botAndQuery().isEmpty() : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainWidget::offerPeer(PeerId peer) {
 | 
			
		||||
	Ui::hideLayer();
 | 
			
		||||
	if (_hider->offerPeer(peer) && Adaptive::OneColumn()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -303,14 +303,17 @@ public:
 | 
			
		|||
	void deleteLayer(int32 selectedCount = -1); // -1 - context item, else selected, -2 - cancel upload
 | 
			
		||||
	void shareContactLayer(UserData *contact);
 | 
			
		||||
	void shareUrlLayer(const QString &url, const QString &text);
 | 
			
		||||
	void inlineSwitchLayer(const QString &botAndQuery);
 | 
			
		||||
	void hiderLayer(HistoryHider *h);
 | 
			
		||||
	void noHider(HistoryHider *destroyed);
 | 
			
		||||
	bool onForward(const PeerId &peer, ForwardWhatMessages what);
 | 
			
		||||
	bool onShareUrl(const PeerId &peer, const QString &url, const QString &text);
 | 
			
		||||
	bool onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery);
 | 
			
		||||
	void onShareContact(const PeerId &peer, UserData *contact);
 | 
			
		||||
	void onSendPaths(const PeerId &peer);
 | 
			
		||||
	void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data);
 | 
			
		||||
	bool selectingPeer(bool withConfirm = false);
 | 
			
		||||
	bool selectingPeerForInlineSwitch();
 | 
			
		||||
	void offerPeer(PeerId peer);
 | 
			
		||||
	void dialogsActivate();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -454,6 +457,7 @@ public:
 | 
			
		|||
	void notify_inlineBotRequesting(bool requesting);
 | 
			
		||||
	void notify_replyMarkupUpdated(const HistoryItem *item);
 | 
			
		||||
	void notify_inlineKeyboardMoved(const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop);
 | 
			
		||||
	void notify_switchInlineBotButtonReceived(const QString &query);
 | 
			
		||||
	void notify_userIsBotChanged(UserData *bot);
 | 
			
		||||
	void notify_userIsContactChanged(UserData *user, bool fromThisApp);
 | 
			
		||||
	void notify_migrateUpdated(PeerData *peer);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1024,8 +1024,11 @@ enum class MTPDmessage_ClientFlag : int32 {
 | 
			
		|||
	// message was sent from inline bot, need to re-set media when sent
 | 
			
		||||
	f_from_inline_bot = (1 << 24),
 | 
			
		||||
 | 
			
		||||
	// message has a switch inline keyboard button, need to return to inline
 | 
			
		||||
	f_has_switch_inline_button = (1 << 23),
 | 
			
		||||
 | 
			
		||||
	// update this when adding new client side flags
 | 
			
		||||
	MIN_FIELD = (1 << 24),
 | 
			
		||||
	MIN_FIELD = (1 << 23),
 | 
			
		||||
};
 | 
			
		||||
DEFINE_MTP_CLIENT_FLAGS(MTPDmessage)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1039,8 +1042,11 @@ enum class MTPDreplyKeyboardMarkup_ClientFlag : int32 {
 | 
			
		|||
	// markup keyboard is inline
 | 
			
		||||
	f_inline = (1 << 28),
 | 
			
		||||
 | 
			
		||||
	// markup has a switch inline keyboard button
 | 
			
		||||
	f_has_switch_inline_button = (1 << 27),
 | 
			
		||||
 | 
			
		||||
	// update this when adding new client side flags
 | 
			
		||||
	MIN_FIELD = (1 << 28),
 | 
			
		||||
	MIN_FIELD = (1 << 27),
 | 
			
		||||
};
 | 
			
		||||
DEFINE_MTP_CLIENT_FLAGS(MTPDreplyKeyboardMarkup)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,16 +156,17 @@ inline bool operator<(const FullMsgId &a, const FullMsgId &b) {
 | 
			
		|||
	return a.channel < b.channel;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const MsgId StartClientMsgId = -0x7FFFFFFF;
 | 
			
		||||
static const MsgId EndClientMsgId = -0x40000000;
 | 
			
		||||
inline bool isClientMsgId(MsgId id) {
 | 
			
		||||
constexpr const MsgId StartClientMsgId = -0x7FFFFFFF;
 | 
			
		||||
constexpr const MsgId EndClientMsgId = -0x40000000;
 | 
			
		||||
inline constexpr bool isClientMsgId(MsgId id) {
 | 
			
		||||
	return id >= StartClientMsgId && id < EndClientMsgId;
 | 
			
		||||
}
 | 
			
		||||
static const MsgId ShowAtTheEndMsgId = -0x40000000;
 | 
			
		||||
static const MsgId SwitchAtTopMsgId = -0x3FFFFFFF;
 | 
			
		||||
static const MsgId ShowAtProfileMsgId = -0x3FFFFFFE;
 | 
			
		||||
static const MsgId ServerMaxMsgId = 0x3FFFFFFF;
 | 
			
		||||
static const MsgId ShowAtUnreadMsgId = 0;
 | 
			
		||||
constexpr const MsgId ShowAtTheEndMsgId = -0x40000000;
 | 
			
		||||
constexpr const MsgId SwitchAtTopMsgId = -0x3FFFFFFF;
 | 
			
		||||
constexpr const MsgId ShowAtProfileMsgId = -0x3FFFFFFE;
 | 
			
		||||
constexpr const MsgId ShowAndStartBotMsgId = -0x3FFFFFD;
 | 
			
		||||
constexpr const MsgId ServerMaxMsgId = 0x3FFFFFFF;
 | 
			
		||||
constexpr const MsgId ShowAtUnreadMsgId = 0;
 | 
			
		||||
 | 
			
		||||
struct NotifySettings {
 | 
			
		||||
	NotifySettings() : flags(MTPDpeerNotifySettings::Flag::f_show_previews), mute(0), sound("default") {
 | 
			
		||||
| 
						 | 
				
			
			@ -373,16 +374,16 @@ private:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
struct BotInfo {
 | 
			
		||||
	BotInfo() : inited(false), readsAllHistory(false), cantJoinGroups(false), version(0), text(st::msgMinWidth) {
 | 
			
		||||
	}
 | 
			
		||||
	bool inited;
 | 
			
		||||
	bool readsAllHistory, cantJoinGroups;
 | 
			
		||||
	int version;
 | 
			
		||||
	bool inited = false;
 | 
			
		||||
	bool readsAllHistory = false;
 | 
			
		||||
	bool cantJoinGroups = false;
 | 
			
		||||
	int version = 0;
 | 
			
		||||
	QString description, inlinePlaceholder;
 | 
			
		||||
	QList<BotCommand> commands;
 | 
			
		||||
	Text text; // description
 | 
			
		||||
	Text text = Text{ int(st::msgMinWidth) }; // description
 | 
			
		||||
 | 
			
		||||
	QString startToken, startGroupToken;
 | 
			
		||||
	PeerId inlineReturnPeerId = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum UserBlockedStatus {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -108,7 +108,9 @@ void TitleWidget::paintEvent(QPaintEvent *e) {
 | 
			
		|||
	if (!_cancel.isHidden()) {
 | 
			
		||||
		p.setPen(st::titleTextButton.color->p);
 | 
			
		||||
		p.setFont(st::titleTextButton.font->f);
 | 
			
		||||
		p.drawText(st::titleMenuOffset - st::titleTextButton.width / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, lang(lng_forward_choose));
 | 
			
		||||
		bool inlineSwitchChoose = (App::main() && App::main()->selectingPeerForInlineSwitch());
 | 
			
		||||
		auto chooseText = lang(inlineSwitchChoose ? lng_inline_switch_choose : lng_forward_choose);
 | 
			
		||||
		p.drawText(st::titleMenuOffset - st::titleTextButton.width / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, chooseText);
 | 
			
		||||
	}
 | 
			
		||||
	p.drawPixmap(st::titleIconPos, App::sprite(), st::titleIconImg);
 | 
			
		||||
	if (Adaptive::OneColumn() && !_counter.isNull() && App::main()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -348,6 +348,23 @@ BoxButton::BoxButton(QWidget *parent, const QString &text, const style::BoxButto
 | 
			
		|||
, a_textBgOverOpacity(0)
 | 
			
		||||
, a_textFg(st.textFg->c)
 | 
			
		||||
, _a_over(animation(this, &BoxButton::step_over)) {
 | 
			
		||||
	resizeToText();
 | 
			
		||||
 | 
			
		||||
	connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource)));
 | 
			
		||||
 | 
			
		||||
	setCursor(style::cur_pointer);
 | 
			
		||||
 | 
			
		||||
	setAttribute(Qt::WA_OpaquePaintEvent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BoxButton::setText(const QString &text) {
 | 
			
		||||
	_text = text;
 | 
			
		||||
	_fullText = text;
 | 
			
		||||
	_textWidth = _st.font->width(_text);
 | 
			
		||||
	resizeToText();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BoxButton::resizeToText() {
 | 
			
		||||
	if (_st.width <= 0) {
 | 
			
		||||
		resize(_textWidth - _st.width, _st.height);
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -357,12 +374,6 @@ BoxButton::BoxButton(QWidget *parent, const QString &text, const style::BoxButto
 | 
			
		|||
		}
 | 
			
		||||
		resize(_st.width, _st.height);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource)));
 | 
			
		||||
 | 
			
		||||
	setCursor(style::cur_pointer);
 | 
			
		||||
 | 
			
		||||
	setAttribute(Qt::WA_OpaquePaintEvent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BoxButton::paintEvent(QPaintEvent *e) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,18 +162,18 @@ class BoxButton : public Button {
 | 
			
		|||
	Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
	BoxButton(QWidget *parent, const QString &text, const style::BoxButton &st);
 | 
			
		||||
 | 
			
		||||
	void paintEvent(QPaintEvent *e);
 | 
			
		||||
	void setText(const QString &text);
 | 
			
		||||
	void paintEvent(QPaintEvent *e) override;
 | 
			
		||||
 | 
			
		||||
	void step_over(float64 ms, bool timer);
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
 | 
			
		||||
	void onStateChange(int oldState, ButtonStateChangeSource source);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void resizeToText();
 | 
			
		||||
 | 
			
		||||
	QString _text, _fullText;
 | 
			
		||||
	int32 _textWidth;
 | 
			
		||||
| 
						 | 
				
			
			@ -183,4 +183,5 @@ private:
 | 
			
		|||
	anim::fvalue a_textBgOverOpacity;
 | 
			
		||||
	anim::cvalue a_textFg;
 | 
			
		||||
	Animation _a_over;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue