mirror of
https://github.com/telegramdesktop/tdesktop
synced 2024-12-29 01:52:48 +00:00
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,9 +4695,11 @@ void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates,
|
||||
_saveEditMsgRequestId = 0;
|
||||
cancelEdit();
|
||||
}
|
||||
if (history->editDraft && history->editDraft->saveRequest == req) {
|
||||
history->setEditDraft(nullptr);
|
||||
writeDrafts(history);
|
||||
if (auto editDraft = history->editDraft()) {
|
||||
if (editDraft->saveRequest == req) {
|
||||
history->clearEditDraft();
|
||||
writeDrafts(history);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4647,8 +4708,10 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp
|
||||
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 {
|
||||
_history->msgDraft->msgId = 0;
|
||||
} else if (auto msgDraft = (_history ? _history->msgDraft() : nullptr)) {
|
||||
if (msgDraft->msgId) {
|
||||
if (msgDraft->text.isEmpty()) {
|
||||
_history->clearMsgDraft();
|
||||
} else {
|
||||
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
Block a user