2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 14:38:15 +00:00

Show send action animations in Replies thread.

This commit is contained in:
John Preston
2020-09-29 11:36:30 +03:00
parent 433c147dd0
commit e8df47c926
20 changed files with 597 additions and 332 deletions

View File

@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history.h"
#include "api/api_send_progress.h"
#include "history/view/history_view_element.h"
#include "history/history_message.h"
#include "history/history_service.h"
@@ -50,18 +49,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kStatusShowClientsideTyping = 6000;
constexpr auto kStatusShowClientsideRecordVideo = 6000;
constexpr auto kStatusShowClientsideUploadVideo = 6000;
constexpr auto kStatusShowClientsideRecordVoice = 6000;
constexpr auto kStatusShowClientsideUploadVoice = 6000;
constexpr auto kStatusShowClientsideRecordRound = 6000;
constexpr auto kStatusShowClientsideUploadRound = 6000;
constexpr auto kStatusShowClientsideUploadPhoto = 6000;
constexpr auto kStatusShowClientsideUploadFile = 6000;
constexpr auto kStatusShowClientsideChooseLocation = 6000;
constexpr auto kStatusShowClientsideChooseContact = 6000;
constexpr auto kStatusShowClientsidePlayGame = 10000;
constexpr auto kNewBlockEachMessage = 50;
constexpr auto kSkipCloudDraftsFor = TimeId(3);
@@ -74,7 +61,7 @@ History::History(not_null<Data::Session*> owner, PeerId peerId)
, peer(owner->peer(peerId))
, cloudDraftTextCache(st::dialogsTextWidthMin)
, _mute(owner->notifyIsMuted(peer))
, _sendActionText(st::dialogsTextWidthMin) {
, _sendActionPainter(this) {
if (const auto user = peer->asUser()) {
if (user->isBot()) {
_outboxReadBefore = std::numeric_limits<MsgId>::max();
@@ -350,219 +337,6 @@ void History::setForwardDraft(MessageIdsList &&items) {
_forwardDraft = std::move(items);
}
bool History::updateSendActionNeedsAnimating(
not_null<UserData*> user,
const MTPSendMessageAction &action) {
if (peer->isSelf()) {
return false;
}
using Type = Api::SendProgressType;
if (action.type() == mtpc_sendMessageCancelAction) {
clearSendAction(user);
return false;
}
const auto now = crl::now();
const auto emplaceAction = [&](
Type type,
crl::time duration,
int progress = 0) {
_sendActions.emplace_or_assign(user, type, now + duration, progress);
};
action.match([&](const MTPDsendMessageTypingAction &) {
_typing.emplace_or_assign(user, now + kStatusShowClientsideTyping);
}, [&](const MTPDsendMessageRecordVideoAction &) {
emplaceAction(Type::RecordVideo, kStatusShowClientsideRecordVideo);
}, [&](const MTPDsendMessageRecordAudioAction &) {
emplaceAction(Type::RecordVoice, kStatusShowClientsideRecordVoice);
}, [&](const MTPDsendMessageRecordRoundAction &) {
emplaceAction(Type::RecordRound, kStatusShowClientsideRecordRound);
}, [&](const MTPDsendMessageGeoLocationAction &) {
emplaceAction(Type::ChooseLocation, kStatusShowClientsideChooseLocation);
}, [&](const MTPDsendMessageChooseContactAction &) {
emplaceAction(Type::ChooseContact, kStatusShowClientsideChooseContact);
}, [&](const MTPDsendMessageUploadVideoAction &data) {
emplaceAction(
Type::UploadVideo,
kStatusShowClientsideUploadVideo,
data.vprogress().v);
}, [&](const MTPDsendMessageUploadAudioAction &data) {
emplaceAction(
Type::UploadVoice,
kStatusShowClientsideUploadVoice,
data.vprogress().v);
}, [&](const MTPDsendMessageUploadRoundAction &data) {
emplaceAction(
Type::UploadRound,
kStatusShowClientsideUploadRound,
data.vprogress().v);
}, [&](const MTPDsendMessageUploadPhotoAction &data) {
emplaceAction(
Type::UploadPhoto,
kStatusShowClientsideUploadPhoto,
data.vprogress().v);
}, [&](const MTPDsendMessageUploadDocumentAction &data) {
emplaceAction(
Type::UploadFile,
kStatusShowClientsideUploadFile,
data.vprogress().v);
}, [&](const MTPDsendMessageGamePlayAction &) {
const auto i = _sendActions.find(user);
if ((i == end(_sendActions))
|| (i->second.type == Type::PlayGame)
|| (i->second.until <= now)) {
emplaceAction(Type::PlayGame, kStatusShowClientsidePlayGame);
}
}, [&](const MTPDsendMessageCancelAction &) {
Unexpected("CancelAction here.");
});
return updateSendActionNeedsAnimating(now, true);
}
bool History::paintSendAction(
Painter &p,
int x,
int y,
int availableWidth,
int outerWidth,
style::color color,
crl::time ms) {
if (_sendActionAnimation) {
_sendActionAnimation.paint(
p,
color,
x,
y + st::normalFont->ascent,
outerWidth,
ms);
auto animationWidth = _sendActionAnimation.width();
x += animationWidth;
availableWidth -= animationWidth;
p.setPen(color);
_sendActionText.drawElided(p, x, y, availableWidth);
return true;
}
return false;
}
bool History::updateSendActionNeedsAnimating(crl::time now, bool force) {
auto changed = force;
for (auto i = begin(_typing); i != end(_typing);) {
if (now >= i->second) {
i = _typing.erase(i);
changed = true;
} else {
++i;
}
}
for (auto i = begin(_sendActions); i != end(_sendActions);) {
if (now >= i->second.until) {
i = _sendActions.erase(i);
changed = true;
} else {
++i;
}
}
if (changed) {
QString newTypingString;
auto typingCount = _typing.size();
if (typingCount > 2) {
newTypingString = tr::lng_many_typing(tr::now, lt_count, typingCount);
} else if (typingCount > 1) {
newTypingString = tr::lng_users_typing(
tr::now,
lt_user,
begin(_typing)->first->firstName,
lt_second_user,
(end(_typing) - 1)->first->firstName);
} else if (typingCount) {
newTypingString = peer->isUser()
? tr::lng_typing(tr::now)
: tr::lng_user_typing(
tr::now,
lt_user,
begin(_typing)->first->firstName);
} else if (!_sendActions.empty()) {
// Handles all actions except game playing.
using Type = Api::SendProgressType;
auto sendActionString = [](Type type, const QString &name) -> QString {
switch (type) {
case Type::RecordVideo: return name.isEmpty() ? tr::lng_send_action_record_video(tr::now) : tr::lng_user_action_record_video(tr::now, lt_user, name);
case Type::UploadVideo: return name.isEmpty() ? tr::lng_send_action_upload_video(tr::now) : tr::lng_user_action_upload_video(tr::now, lt_user, name);
case Type::RecordVoice: return name.isEmpty() ? tr::lng_send_action_record_audio(tr::now) : tr::lng_user_action_record_audio(tr::now, lt_user, name);
case Type::UploadVoice: return name.isEmpty() ? tr::lng_send_action_upload_audio(tr::now) : tr::lng_user_action_upload_audio(tr::now, lt_user, name);
case Type::RecordRound: return name.isEmpty() ? tr::lng_send_action_record_round(tr::now) : tr::lng_user_action_record_round(tr::now, lt_user, name);
case Type::UploadRound: return name.isEmpty() ? tr::lng_send_action_upload_round(tr::now) : tr::lng_user_action_upload_round(tr::now, lt_user, name);
case Type::UploadPhoto: return name.isEmpty() ? tr::lng_send_action_upload_photo(tr::now) : tr::lng_user_action_upload_photo(tr::now, lt_user, name);
case Type::UploadFile: return name.isEmpty() ? tr::lng_send_action_upload_file(tr::now) : tr::lng_user_action_upload_file(tr::now, lt_user, name);
case Type::ChooseLocation:
case Type::ChooseContact: return name.isEmpty() ? tr::lng_typing(tr::now) : tr::lng_user_typing(tr::now, lt_user, name);
default: break;
};
return QString();
};
for (const auto [user, action] : _sendActions) {
newTypingString = sendActionString(
action.type,
peer->isUser() ? QString() : user->firstName);
if (!newTypingString.isEmpty()) {
_sendActionAnimation.start(action.type);
break;
}
}
// Everyone in sendActions are playing a game.
if (newTypingString.isEmpty()) {
int playingCount = _sendActions.size();
if (playingCount > 2) {
newTypingString = tr::lng_many_playing_game(
tr::now,
lt_count,
playingCount);
} else if (playingCount > 1) {
newTypingString = tr::lng_users_playing_game(
tr::now,
lt_user,
begin(_sendActions)->first->firstName,
lt_second_user,
(end(_sendActions) - 1)->first->firstName);
} else {
newTypingString = peer->isUser()
? tr::lng_playing_game(tr::now)
: tr::lng_user_playing_game(
tr::now,
lt_user,
begin(_sendActions)->first->firstName);
}
_sendActionAnimation.start(Type::PlayGame);
}
}
if (typingCount > 0) {
_sendActionAnimation.start(Api::SendProgressType::Typing);
} else if (newTypingString.isEmpty()) {
_sendActionAnimation.stop();
}
if (_sendActionString != newTypingString) {
_sendActionString = newTypingString;
_sendActionText.setText(
st::dialogsTextStyle,
_sendActionString,
Ui::NameTextOptions());
}
}
const auto result = (!_typing.empty() || !_sendActions.empty());
if (changed || (result && !anim::Disabled())) {
owner().updateSendActionAnimation({
this,
_sendActionAnimation.width(),
st::normalFont->height,
changed
});
}
return result;
}
HistoryItem *History::createItem(
const MTPMessage &message,
MTPDmessage_ClientFlags clientFlags,
@@ -1229,23 +1003,6 @@ void History::applyServiceChanges(
}
}
void History::clearSendAction(not_null<UserData*> from) {
auto updateAtMs = crl::time(0);
auto i = _typing.find(from);
if (i != _typing.cend()) {
updateAtMs = crl::now();
i->second = updateAtMs;
}
auto j = _sendActions.find(from);
if (j != _sendActions.cend()) {
if (!updateAtMs) updateAtMs = crl::now();
j->second.until = updateAtMs;
}
if (updateAtMs) {
updateSendActionNeedsAnimating(updateAtMs, true);
}
}
void History::mainViewRemoved(
not_null<HistoryBlock*> block,
not_null<HistoryView::Element*> view) {
@@ -1266,7 +1023,8 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
item->indexAsNewItem();
if (const auto from = item->from() ? item->from()->asUser() : nullptr) {
if (from == item->author()) {
clearSendAction(from);
_sendActionPainter.clear(from);
owner().repliesSendActionPaintersClear(this, from);
}
from->madeAction(item->date());
}