mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Make messages nicer, sending animation.
This commit is contained in:
@@ -1687,6 +1687,8 @@ groupCallInviteLinkIcon: icon {{ "info/edit/group_manage_links", mediaviewTextLi
|
||||
|
||||
groupCallMessagePadding: margins(8px, 2px, 8px, 2px);
|
||||
groupCallMessageSkip: 8px;
|
||||
groupCallUserpic: 20px;
|
||||
groupCallUserpicPadding: margins(2px, 2px, 4px, 2px);
|
||||
|
||||
confcallLinkMenu: IconButton(boxTitleClose) {
|
||||
icon: icon {{ "title_menu_dots", boxTitleCloseFg }};
|
||||
|
||||
@@ -20,10 +20,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/ui_utility.h"
|
||||
|
||||
namespace Calls::Group {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMessageLifetime = TimeId(20);
|
||||
|
||||
} // namespace
|
||||
|
||||
Messages::Messages(not_null<GroupCall*> call, not_null<MTP::Sender*> api)
|
||||
: _call(call)
|
||||
, _api(api) {
|
||||
, _api(api)
|
||||
, _destroyTimer([=] { checkDestroying(); }) {
|
||||
Ui::PostponeCall(_call, [=] {
|
||||
_call->real(
|
||||
) | rpl::start_with_next([=](not_null<Data::GroupCall*> call) {
|
||||
@@ -71,9 +77,13 @@ void Messages::send(TextWithTags text) {
|
||||
_call->inputCall(),
|
||||
serialized
|
||||
)).done([=](const MTPBool &, const MTP::Response &response) {
|
||||
if (rand() % 256 < 64) {
|
||||
AssertIsDebug();
|
||||
failed(id, response);
|
||||
} else
|
||||
sent(id, response);
|
||||
}).fail([=] {
|
||||
failed(id);
|
||||
}).fail([=](const MTP::Error &, const MTP::Response &response) {
|
||||
failed(id, response);
|
||||
}).send();
|
||||
} else {
|
||||
auto counter = ::tl::details::LengthCounter();
|
||||
@@ -94,11 +104,11 @@ void Messages::send(TextWithTags text) {
|
||||
MTP_bytes(bytes::make_span(encrypted))
|
||||
)).done([=](const MTPBool &, const MTP::Response &response) {
|
||||
sent(id, response);
|
||||
}).fail([=] {
|
||||
failed(id);
|
||||
}).fail([=](const MTP::Error &, const MTP::Response &response) {
|
||||
failed(id, response);
|
||||
}).send();
|
||||
}
|
||||
pushChanges();
|
||||
checkDestroying(true);
|
||||
}
|
||||
|
||||
void Messages::received(const MTPDupdateGroupCallMessage &data) {
|
||||
@@ -152,6 +162,39 @@ void Messages::received(
|
||||
.peer = peer->owner().peer(peerFromMTP(from)),
|
||||
.text = Api::ParseTextWithEntities(&peer->session(), message),
|
||||
});
|
||||
checkDestroying(true);
|
||||
}
|
||||
|
||||
void Messages::checkDestroying(bool afterChanges) {
|
||||
auto next = TimeId();
|
||||
const auto now = base::unixtime::now();
|
||||
const auto destroyTime = now - kMessageLifetime;
|
||||
const auto initial = _messages.size();
|
||||
for (auto i = begin(_messages); i != end(_messages);) {
|
||||
const auto date = i->date;
|
||||
if (!date) {
|
||||
++i;
|
||||
} else if (date <= destroyTime) {
|
||||
i = _messages.erase(i);
|
||||
} else if (!next) {
|
||||
next = date + kMessageLifetime - now;
|
||||
++i;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (!next) {
|
||||
_destroyTimer.cancel();
|
||||
} else {
|
||||
const auto delay = next * crl::time(1000);
|
||||
if (!_destroyTimer.isActive()
|
||||
|| (_destroyTimer.remainingTime() > delay)) {
|
||||
_destroyTimer.callOnce(delay);
|
||||
}
|
||||
}
|
||||
if (afterChanges || (_messages.size() < initial)) {
|
||||
pushChanges();
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<Message>> Messages::listValue() const {
|
||||
@@ -174,15 +217,16 @@ void Messages::sent(int id, const MTP::Response &response) {
|
||||
const auto i = ranges::find(_messages, id, &Message::id);
|
||||
if (i != end(_messages)) {
|
||||
i->date = Api::UnixtimeFromMsgId(response.outerMsgId);
|
||||
pushChanges();
|
||||
checkDestroying(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Messages::failed(int id) {
|
||||
void Messages::failed(int id, const MTP::Response &response) {
|
||||
const auto i = ranges::find(_messages, id, &Message::id);
|
||||
if (i != end(_messages)) {
|
||||
i->date = Api::UnixtimeFromMsgId(response.outerMsgId);
|
||||
i->failed = true;
|
||||
pushChanges();
|
||||
checkDestroying(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace Calls {
|
||||
class GroupCall;
|
||||
} // namespace Calls
|
||||
@@ -45,10 +47,11 @@ private:
|
||||
[[nodiscard]] bool ready() const;
|
||||
void sendPending();
|
||||
void pushChanges();
|
||||
void checkDestroying(bool afterChanges = false);
|
||||
|
||||
void received(const MTPPeer &from, const MTPTextWithEntities &message);
|
||||
void sent(int id, const MTP::Response &response);
|
||||
void failed(int id);
|
||||
void failed(int id, const MTP::Response &response);
|
||||
|
||||
const not_null<GroupCall*> _call;
|
||||
const not_null<MTP::Sender*> _api;
|
||||
@@ -57,6 +60,7 @@ private:
|
||||
|
||||
std::vector<TextWithTags> _pending;
|
||||
|
||||
base::Timer _destroyTimer;
|
||||
std::vector<Message> _messages;
|
||||
rpl::event_stream<std::vector<Message>> _changes;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/controls/emoji_button.h"
|
||||
#include "ui/controls/send_button.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
@@ -55,7 +56,7 @@ struct MessagesUi::MessageView {
|
||||
PeerData *from = nullptr;
|
||||
Ui::Animations::Simple toggleAnimation;
|
||||
Ui::Animations::Simple sentAnimation;
|
||||
Ui::Animations::Basic sendingAnimation;
|
||||
std::unique_ptr<Ui::InfiniteRadialAnimation> sendingAnimation;
|
||||
Ui::PeerUserpicView view;
|
||||
Ui::Text::String text;
|
||||
int top = 0;
|
||||
@@ -97,8 +98,10 @@ void MessagesUi::setupList(rpl::producer<std::vector<Message>> messages) {
|
||||
const auto i = ranges::find(from, till, id, &Message::id);
|
||||
if (i == till) {
|
||||
toggleMessage(entry, false);
|
||||
continue;
|
||||
} else if (entry.failed != i->failed) {
|
||||
setContentFailed(entry);
|
||||
updateMessageSize(entry);
|
||||
repaintMessage(entry.id);
|
||||
} else if (entry.sending != (i->date == 0)) {
|
||||
animateMessageSent(entry);
|
||||
@@ -121,33 +124,56 @@ void MessagesUi::animateMessageSent(MessageView &entry) {
|
||||
entry.sending = false;
|
||||
entry.sentAnimation.start([=] {
|
||||
repaintMessage(id);
|
||||
}, 0., 1, st::slideWrapDuration, anim::easeOutCirc);
|
||||
}, 0., 1, st::slideDuration, anim::easeOutCirc);
|
||||
repaintMessage(id);
|
||||
}
|
||||
|
||||
void MessagesUi::updateMessageSize(MessageView &entry) {
|
||||
const auto &padding = st::groupCallMessagePadding;
|
||||
|
||||
const auto widthSkip = padding.left() + padding.right();
|
||||
const auto hasUserpic = !entry.failed;
|
||||
const auto userpicPadding = st::groupCallUserpicPadding;
|
||||
const auto userpicSize = st::groupCallUserpic;
|
||||
const auto leftSkip = hasUserpic
|
||||
? (userpicPadding.left() + userpicSize + userpicPadding.right())
|
||||
: padding.left();
|
||||
const auto widthSkip = leftSkip + padding.right();
|
||||
const auto inner = _width - widthSkip;
|
||||
entry.width = std::min(inner, entry.text.maxWidth()) + widthSkip;
|
||||
|
||||
const auto size = Ui::Text::CountOptimalTextSize(
|
||||
entry.text,
|
||||
std::min(st::groupCallWidth / 2, inner),
|
||||
inner);
|
||||
|
||||
const auto textHeight = size.height();
|
||||
entry.width = size.width() + widthSkip;
|
||||
entry.left = (_width - entry.width) / 2;
|
||||
|
||||
const auto contentHeight = padding.top() + textHeight + padding.bottom();
|
||||
const auto userpicHeight = hasUserpic
|
||||
? (userpicPadding.top() + userpicSize + userpicPadding.bottom())
|
||||
: 0;
|
||||
|
||||
const auto skip = st::groupCallMessageSkip;
|
||||
const auto textHeight = entry.text.countHeight(entry.width);
|
||||
entry.realHeight = skip
|
||||
+ padding.top()
|
||||
+ textHeight
|
||||
+ padding.bottom();
|
||||
entry.height = entry.toggleAnimation.animating()
|
||||
entry.realHeight = skip + std::max(contentHeight, userpicHeight);
|
||||
}
|
||||
|
||||
bool MessagesUi::updateMessageHeight(MessageView &entry) {
|
||||
const auto height = entry.toggleAnimation.animating()
|
||||
? anim::interpolate(
|
||||
0,
|
||||
entry.realHeight,
|
||||
entry.toggleAnimation.value(entry.removed ? 0. : 1.))
|
||||
: entry.realHeight;
|
||||
if (entry.height == height) {
|
||||
return false;
|
||||
}
|
||||
entry.height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessagesUi::setContentFailed(MessageView &entry) {
|
||||
const auto manager = &entry.from->owner().customEmojiManager();
|
||||
entry.failed = true;
|
||||
entry.text = Ui::Text::String(
|
||||
st::messageTextStyle,
|
||||
TextWithEntities().append(
|
||||
@@ -155,7 +181,21 @@ void MessagesUi::setContentFailed(MessageView &entry) {
|
||||
).append(' ').append(
|
||||
Ui::Text::Italic(u"Failed to send the message."_q)),
|
||||
kMarkupTextOptions,
|
||||
st::groupCallWidth / 2);
|
||||
st::groupCallWidth / 4);
|
||||
}
|
||||
|
||||
void MessagesUi::setContent(
|
||||
MessageView &entry,
|
||||
const TextWithEntities &text) {
|
||||
entry.text = Ui::Text::String(
|
||||
st::messageTextStyle,
|
||||
text,
|
||||
kMarkupTextOptions,
|
||||
st::groupCallWidth / 4,
|
||||
Core::TextContext({
|
||||
.session = &_show->session(),
|
||||
.repaint = [this, id = entry.id] { repaintMessage(id); },
|
||||
}));
|
||||
}
|
||||
|
||||
void MessagesUi::toggleMessage(MessageView &entry, bool shown) {
|
||||
@@ -166,7 +206,8 @@ void MessagesUi::toggleMessage(MessageView &entry, bool shown) {
|
||||
shown ? 0. : 1.,
|
||||
shown ? 1. : 0.,
|
||||
st::slideWrapDuration,
|
||||
anim::easeOutCirc);
|
||||
shown ? anim::easeOutCirc : anim::easeInCirc);
|
||||
repaintMessage(id);
|
||||
}
|
||||
|
||||
void MessagesUi::repaintMessage(int id) {
|
||||
@@ -180,13 +221,10 @@ void MessagesUi::repaintMessage(int id) {
|
||||
return;
|
||||
}
|
||||
if (!i->sending && !i->sentAnimation.animating()) {
|
||||
i->sendingAnimation.stop();
|
||||
i->sendingAnimation = nullptr;
|
||||
}
|
||||
if (i->toggleAnimation.animating() || i->height != i->realHeight) {
|
||||
const auto shown = i->toggleAnimation.value(i->removed ? 0. : 1.);
|
||||
const auto height = anim::interpolate(0, i->realHeight, shown);
|
||||
if (i->height != height) {
|
||||
i->height = height;
|
||||
if (updateMessageHeight(*i)) {
|
||||
recountHeights(i, i->top);
|
||||
return;
|
||||
}
|
||||
@@ -227,30 +265,23 @@ void MessagesUi::appendMessage(const Message &data) {
|
||||
};
|
||||
entry.from = data.peer;
|
||||
entry.sending = !data.date;
|
||||
entry.failed = data.failed;
|
||||
if (entry.failed) {
|
||||
if (data.failed) {
|
||||
setContentFailed(entry);
|
||||
} else {
|
||||
const auto manager = &entry.from->owner().customEmojiManager();
|
||||
entry.text = Ui::Text::String(
|
||||
st::messageTextStyle,
|
||||
Ui::Text::SingleCustomEmoji(
|
||||
manager->peerUserpicEmojiData(entry.from),
|
||||
u"@"_q).append(' ').append(
|
||||
Ui::Text::Bold(data.peer->shortName())
|
||||
).append(' ').append(data.text),
|
||||
kMarkupTextOptions,
|
||||
st::groupCallWidth / 2,
|
||||
Core::TextContext({
|
||||
.session = &_show->session(),
|
||||
.repaint = repaint,
|
||||
}));
|
||||
setContent(
|
||||
entry,
|
||||
Ui::Text::Bold(
|
||||
data.peer->shortName()).append(' ').append(data.text));
|
||||
}
|
||||
entry.top = top;
|
||||
updateMessageSize(entry);
|
||||
if (entry.sending) {
|
||||
entry.sendingAnimation.init(repaint);
|
||||
entry.sendingAnimation.start();
|
||||
using namespace Ui;
|
||||
const auto &st = st::defaultInfiniteRadialAnimation;
|
||||
entry.sendingAnimation = std::make_unique<InfiniteRadialAnimation>(
|
||||
repaint,
|
||||
st);
|
||||
entry.sendingAnimation->start(0);
|
||||
}
|
||||
entry.height = 0;
|
||||
toggleMessage(entry, true);
|
||||
@@ -262,8 +293,7 @@ void MessagesUi::setupMessagesWidget() {
|
||||
object_ptr<Ui::RpWidget>(_scroll.get()));
|
||||
|
||||
_messages->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(_messages);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
auto p = Painter(_messages);
|
||||
const auto skip = st::groupCallMessageSkip;
|
||||
const auto padding = st::groupCallMessagePadding;
|
||||
const auto widthSkip = padding.left() + padding.right();
|
||||
@@ -289,10 +319,49 @@ void MessagesUi::setupMessagesWidget() {
|
||||
}
|
||||
_messageBgRect.paint(p, { entry.left, entry.top, width, use });
|
||||
|
||||
auto leftSkip = padding.left();
|
||||
const auto hasUserpic = !entry.failed;
|
||||
if (hasUserpic) {
|
||||
const auto userpicSize = st::groupCallUserpic;
|
||||
const auto userpicPadding = st::groupCallUserpicPadding;
|
||||
const auto position = QPoint(
|
||||
entry.left + userpicPadding.left(),
|
||||
entry.top + userpicPadding.top());
|
||||
const auto rect = QRect(
|
||||
position,
|
||||
QSize(userpicSize, userpicSize));
|
||||
entry.from->paintUserpic(p, entry.view, {
|
||||
.position = position,
|
||||
.size = userpicSize,
|
||||
.shape = Ui::PeerUserpicShape::Circle,
|
||||
});
|
||||
if (const auto animation = entry.sendingAnimation.get()) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
auto pen = st::groupCallBg->p;
|
||||
const auto shift = userpicPadding.left();
|
||||
pen.setWidthF(shift);
|
||||
p.setPen(pen);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
const auto state = animation->computeState();
|
||||
const auto sent = entry.sending
|
||||
? 0.
|
||||
: entry.sentAnimation.value(1.);
|
||||
p.setOpacity(state.shown * (1. - sent));
|
||||
p.drawArc(
|
||||
rect.marginsRemoved({ shift, shift, shift, shift }),
|
||||
state.arcFrom,
|
||||
state.arcLength);
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
leftSkip = userpicPadding.left()
|
||||
+ userpicSize
|
||||
+ userpicPadding.right();
|
||||
}
|
||||
|
||||
p.setPen(st::radialFg);
|
||||
entry.text.draw(p, {
|
||||
.position = {
|
||||
entry.left + padding.left(),
|
||||
entry.left + leftSkip,
|
||||
entry.top + padding.top()
|
||||
},
|
||||
.availableWidth = entry.width - widthSkip,
|
||||
@@ -313,8 +382,11 @@ void MessagesUi::applyWidth() {
|
||||
}
|
||||
auto top = 0;
|
||||
for (auto &entry : _views) {
|
||||
updateMessageSize(entry);
|
||||
entry.top = top;
|
||||
|
||||
updateMessageSize(entry);
|
||||
updateMessageHeight(entry);
|
||||
|
||||
top += entry.height;
|
||||
}
|
||||
updateGeometries();
|
||||
|
||||
@@ -48,7 +48,9 @@ private:
|
||||
void setupList(rpl::producer<std::vector<Message>> messages);
|
||||
void toggleMessage(MessageView &entry, bool shown);
|
||||
void setContentFailed(MessageView &entry);
|
||||
void setContent(MessageView &entry, const TextWithEntities &text);
|
||||
void updateMessageSize(MessageView &entry);
|
||||
bool updateMessageHeight(MessageView &entry);
|
||||
void animateMessageSent(MessageView &entry);
|
||||
void repaintMessage(int id);
|
||||
void recountHeights(std::vector<MessageView>::iterator i, int top);
|
||||
|
||||
@@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "ui/painter.h"
|
||||
|
||||
Reference in New Issue
Block a user