Play premium video avatars in chats list.
This commit is contained in:
parent
5478a8c014
commit
201edb2e69
|
@ -544,6 +544,8 @@ PRIVATE
|
|||
dialogs/ui/dialogs_layout.h
|
||||
dialogs/ui/dialogs_message_view.cpp
|
||||
dialogs/ui/dialogs_message_view.h
|
||||
dialogs/ui/dialogs_video_userpic.cpp
|
||||
dialogs/ui/dialogs_video_userpic.h
|
||||
editor/color_picker.cpp
|
||||
editor/color_picker.h
|
||||
editor/controllers/controllers.h
|
||||
|
|
|
@ -92,7 +92,10 @@ ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
|
|||
|
||||
void ChannelData::setPhoto(const MTPChatPhoto &photo) {
|
||||
photo.match([&](const MTPDchatPhoto & data) {
|
||||
updateUserpic(data.vphoto_id().v, data.vdc_id().v);
|
||||
updateUserpic(
|
||||
data.vphoto_id().v,
|
||||
data.vdc_id().v,
|
||||
data.is_has_video());
|
||||
}, [&](const MTPDchatPhotoEmpty &) {
|
||||
clearUserpic();
|
||||
});
|
||||
|
|
|
@ -39,7 +39,10 @@ ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
|
|||
|
||||
void ChatData::setPhoto(const MTPChatPhoto &photo) {
|
||||
photo.match([&](const MTPDchatPhoto &data) {
|
||||
updateUserpic(data.vphoto_id().v, data.vdc_id().v);
|
||||
updateUserpic(
|
||||
data.vphoto_id().v,
|
||||
data.vdc_id().v,
|
||||
data.is_has_video());
|
||||
}, [&](const MTPDchatPhotoEmpty &) {
|
||||
clearUserpic();
|
||||
});
|
||||
|
|
|
@ -307,8 +307,12 @@ ClickHandlerPtr PeerData::createOpenLink() {
|
|||
return std::make_shared<PeerClickHandler>(this);
|
||||
}
|
||||
|
||||
void PeerData::setUserpic(PhotoId photoId, const ImageLocation &location) {
|
||||
void PeerData::setUserpic(
|
||||
PhotoId photoId,
|
||||
const ImageLocation &location,
|
||||
bool hasVideo) {
|
||||
_userpicPhotoId = photoId;
|
||||
_userpicHasVideo = hasVideo;
|
||||
_userpic.set(&session(), ImageWithLocation{ .location = location });
|
||||
}
|
||||
|
||||
|
@ -479,7 +483,10 @@ Data::FileOrigin PeerData::userpicPhotoOrigin() const {
|
|||
: Data::FileOrigin();
|
||||
}
|
||||
|
||||
void PeerData::updateUserpic(PhotoId photoId, MTP::DcId dcId) {
|
||||
void PeerData::updateUserpic(
|
||||
PhotoId photoId,
|
||||
MTP::DcId dcId,
|
||||
bool hasVideo) {
|
||||
setUserpicChecked(
|
||||
photoId,
|
||||
ImageLocation(
|
||||
|
@ -491,19 +498,27 @@ void PeerData::updateUserpic(PhotoId photoId, MTP::DcId dcId) {
|
|||
input,
|
||||
MTP_long(photoId))) },
|
||||
kUserpicSize,
|
||||
kUserpicSize));
|
||||
kUserpicSize),
|
||||
hasVideo);
|
||||
}
|
||||
|
||||
void PeerData::clearUserpic() {
|
||||
setUserpicChecked(PhotoId(), ImageLocation());
|
||||
setUserpicChecked(PhotoId(), ImageLocation(), false);
|
||||
}
|
||||
|
||||
void PeerData::setUserpicChecked(
|
||||
PhotoId photoId,
|
||||
const ImageLocation &location) {
|
||||
if (_userpicPhotoId != photoId || _userpic.location() != location) {
|
||||
setUserpic(photoId, location);
|
||||
const ImageLocation &location,
|
||||
bool hasVideo) {
|
||||
if (_userpicPhotoId != photoId
|
||||
|| _userpic.location() != location
|
||||
|| _userpicHasVideo != hasVideo) {
|
||||
const auto known = !userpicPhotoUnknown();
|
||||
setUserpic(photoId, location, hasVideo);
|
||||
session().changes().peerUpdated(this, UpdateFlag::Photo);
|
||||
if (known && isPremium() && userpicPhotoUnknown()) {
|
||||
updateFull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -849,6 +864,13 @@ bool PeerData::isVerified() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::isPremium() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->isPremium();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::isScam() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->isScam();
|
||||
|
|
|
@ -172,6 +172,7 @@ public:
|
|||
}
|
||||
[[nodiscard]] bool isSelf() const;
|
||||
[[nodiscard]] bool isVerified() const;
|
||||
[[nodiscard]] bool isPremium() const;
|
||||
[[nodiscard]] bool isScam() const;
|
||||
[[nodiscard]] bool isFake() const;
|
||||
[[nodiscard]] bool isMegagroup() const;
|
||||
|
@ -266,7 +267,10 @@ public:
|
|||
return _nameFirstLetters;
|
||||
}
|
||||
|
||||
void setUserpic(PhotoId photoId, const ImageLocation &location);
|
||||
void setUserpic(
|
||||
PhotoId photoId,
|
||||
const ImageLocation &location,
|
||||
bool hasVideo);
|
||||
void setUserpicPhoto(const MTPPhoto &data);
|
||||
void paintUserpic(
|
||||
Painter &p,
|
||||
|
@ -320,6 +324,9 @@ public:
|
|||
[[nodiscard]] PhotoId userpicPhotoId() const {
|
||||
return userpicPhotoUnknown() ? 0 : _userpicPhotoId;
|
||||
}
|
||||
[[nodiscard]] bool userpicHasVideo() const {
|
||||
return _userpicHasVideo;
|
||||
}
|
||||
[[nodiscard]] Data::FileOrigin userpicOrigin() const;
|
||||
[[nodiscard]] Data::FileOrigin userpicPhotoOrigin() const;
|
||||
|
||||
|
@ -426,7 +433,7 @@ protected:
|
|||
const QString &newName,
|
||||
const QString &newNameOrPhone,
|
||||
const QString &newUsername);
|
||||
void updateUserpic(PhotoId photoId, MTP::DcId dcId);
|
||||
void updateUserpic(PhotoId photoId, MTP::DcId dcId, bool hasVideo);
|
||||
void clearUserpic();
|
||||
|
||||
private:
|
||||
|
@ -435,12 +442,17 @@ private:
|
|||
[[nodiscard]] virtual auto unavailableReasons() const
|
||||
-> const std::vector<Data::UnavailableReason> &;
|
||||
|
||||
void setUserpicChecked(PhotoId photoId, const ImageLocation &location);
|
||||
void setUserpicChecked(
|
||||
PhotoId photoId,
|
||||
const ImageLocation &location,
|
||||
bool hasVideo);
|
||||
|
||||
const not_null<Data::Session*> _owner;
|
||||
|
||||
mutable Data::CloudImage _userpic;
|
||||
PhotoId _userpicPhotoId = kUnknownPhotoId;
|
||||
bool _userpicHasVideo = false;
|
||||
|
||||
mutable std::unique_ptr<Ui::EmptyUserpic> _userpicEmpty;
|
||||
Ui::Text::String _nameText;
|
||||
|
||||
|
|
|
@ -47,7 +47,10 @@ void UserData::setIsContact(bool is) {
|
|||
// see Serialize::readPeer as well
|
||||
void UserData::setPhoto(const MTPUserProfilePhoto &photo) {
|
||||
photo.match([&](const MTPDuserProfilePhoto &data) {
|
||||
updateUserpic(data.vphoto_id().v, data.vdc_id().v);
|
||||
updateUserpic(
|
||||
data.vphoto_id().v,
|
||||
data.vdc_id().v,
|
||||
data.is_has_video());
|
||||
}, [&](const MTPDuserProfilePhotoEmpty &) {
|
||||
clearUserpic();
|
||||
});
|
||||
|
@ -191,6 +194,75 @@ void UserData::removeFlags(UserDataFlags which) {
|
|||
_flags.remove(which & ~UserDataFlag::Self);
|
||||
}
|
||||
|
||||
bool UserData::isVerified() const {
|
||||
return flags() & UserDataFlag::Verified;
|
||||
}
|
||||
|
||||
bool UserData::isScam() const {
|
||||
return flags() & UserDataFlag::Scam;
|
||||
}
|
||||
|
||||
bool UserData::isFake() const {
|
||||
return flags() & UserDataFlag::Fake;
|
||||
}
|
||||
|
||||
bool UserData::isPremium() const {
|
||||
return flags() & UserDataFlag::Premium;
|
||||
}
|
||||
|
||||
bool UserData::isBotInlineGeo() const {
|
||||
return flags() & UserDataFlag::BotInlineGeo;
|
||||
}
|
||||
|
||||
bool UserData::isBot() const {
|
||||
return botInfo != nullptr;
|
||||
}
|
||||
|
||||
bool UserData::isSupport() const {
|
||||
return flags() & UserDataFlag::Support;
|
||||
}
|
||||
|
||||
bool UserData::isInaccessible() const {
|
||||
return flags() & UserDataFlag::Deleted;
|
||||
}
|
||||
|
||||
bool UserData::canWrite() const {
|
||||
// Duplicated in Data::CanWriteValue().
|
||||
return !isInaccessible() && !isRepliesChat();
|
||||
}
|
||||
|
||||
bool UserData::applyMinPhoto() const {
|
||||
return !(flags() & UserDataFlag::DiscardMinPhoto);
|
||||
}
|
||||
|
||||
bool UserData::canAddContact() const {
|
||||
return canShareThisContact() && !isContact();
|
||||
}
|
||||
|
||||
bool UserData::canShareThisContactFast() const {
|
||||
return !_phone.isEmpty();
|
||||
}
|
||||
|
||||
const QString &UserData::phone() const {
|
||||
return _phone;
|
||||
}
|
||||
|
||||
UserData::ContactStatus UserData::contactStatus() const {
|
||||
return _contactStatus;
|
||||
}
|
||||
|
||||
bool UserData::isContact() const {
|
||||
return (contactStatus() == ContactStatus::Contact);
|
||||
}
|
||||
|
||||
UserData::CallsStatus UserData::callsStatus() const {
|
||||
return _callsStatus;
|
||||
}
|
||||
|
||||
int UserData::commonChatsCount() const {
|
||||
return _commonChatsCount;
|
||||
}
|
||||
|
||||
void UserData::setCallsStatus(CallsStatus callsStatus) {
|
||||
if (callsStatus != _callsStatus) {
|
||||
_callsStatus = callsStatus;
|
||||
|
|
|
@ -88,58 +88,31 @@ public:
|
|||
void addFlags(UserDataFlags which);
|
||||
void removeFlags(UserDataFlags which);
|
||||
|
||||
[[nodiscard]] bool isVerified() const {
|
||||
return flags() & UserDataFlag::Verified;
|
||||
}
|
||||
[[nodiscard]] bool isScam() const {
|
||||
return flags() & UserDataFlag::Scam;
|
||||
}
|
||||
[[nodiscard]] bool isFake() const {
|
||||
return flags() & UserDataFlag::Fake;
|
||||
}
|
||||
[[nodiscard]] bool isPremium() const {
|
||||
return flags() & UserDataFlag::Premium;
|
||||
}
|
||||
[[nodiscard]] bool isBotInlineGeo() const {
|
||||
return flags() & UserDataFlag::BotInlineGeo;
|
||||
}
|
||||
[[nodiscard]] bool isBot() const {
|
||||
return botInfo != nullptr;
|
||||
}
|
||||
[[nodiscard]] bool isSupport() const {
|
||||
return flags() & UserDataFlag::Support;
|
||||
}
|
||||
[[nodiscard]] bool isInaccessible() const {
|
||||
return flags() & UserDataFlag::Deleted;
|
||||
}
|
||||
[[nodiscard]] bool canWrite() const {
|
||||
// Duplicated in Data::CanWriteValue().
|
||||
return !isInaccessible() && !isRepliesChat();
|
||||
}
|
||||
[[nodiscard]] bool applyMinPhoto() const {
|
||||
return !(flags() & UserDataFlag::DiscardMinPhoto);
|
||||
}
|
||||
[[nodiscard]] bool isVerified() const;
|
||||
[[nodiscard]] bool isScam() const;
|
||||
[[nodiscard]] bool isFake() const;
|
||||
[[nodiscard]] bool isPremium() const;
|
||||
[[nodiscard]] bool isBotInlineGeo() const;
|
||||
[[nodiscard]] bool isBot() const;
|
||||
[[nodiscard]] bool isSupport() const;
|
||||
[[nodiscard]] bool isInaccessible() const;
|
||||
[[nodiscard]] bool canWrite() const;
|
||||
[[nodiscard]] bool applyMinPhoto() const;
|
||||
|
||||
[[nodiscard]] bool canShareThisContact() const;
|
||||
[[nodiscard]] bool canAddContact() const {
|
||||
return canShareThisContact() && !isContact();
|
||||
}
|
||||
[[nodiscard]] bool canAddContact() const;
|
||||
|
||||
// In Data::Session::processUsers() we check only that.
|
||||
// When actually trying to share contact we perform
|
||||
// a full check by canShareThisContact() call.
|
||||
[[nodiscard]] bool canShareThisContactFast() const {
|
||||
return !_phone.isEmpty();
|
||||
}
|
||||
[[nodiscard]] bool canShareThisContactFast() const;
|
||||
|
||||
MTPInputUser inputUser = MTP_inputUserEmpty();
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
QString username;
|
||||
[[nodiscard]] const QString &phone() const {
|
||||
return _phone;
|
||||
}
|
||||
[[nodiscard]] const QString &phone() const;
|
||||
QString nameOrPhone;
|
||||
Ui::Text::String phoneText;
|
||||
TimeId onlineTill = 0;
|
||||
|
@ -149,12 +122,8 @@ public:
|
|||
Contact,
|
||||
NotContact,
|
||||
};
|
||||
[[nodiscard]] ContactStatus contactStatus() const {
|
||||
return _contactStatus;
|
||||
}
|
||||
[[nodiscard]] bool isContact() const {
|
||||
return (contactStatus() == ContactStatus::Contact);
|
||||
}
|
||||
[[nodiscard]] ContactStatus contactStatus() const;
|
||||
[[nodiscard]] bool isContact() const;
|
||||
void setIsContact(bool is);
|
||||
|
||||
enum class CallsStatus : char {
|
||||
|
@ -163,9 +132,7 @@ public:
|
|||
Disabled,
|
||||
Private,
|
||||
};
|
||||
CallsStatus callsStatus() const {
|
||||
return _callsStatus;
|
||||
}
|
||||
CallsStatus callsStatus() const;
|
||||
bool hasCalls() const;
|
||||
void setCallsStatus(CallsStatus callsStatus);
|
||||
|
||||
|
@ -174,9 +141,7 @@ public:
|
|||
void setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reasons);
|
||||
|
||||
int commonChatsCount() const {
|
||||
return _commonChatsCount;
|
||||
}
|
||||
int commonChatsCount() const;
|
||||
void setCommonChatsCount(int count);
|
||||
|
||||
private:
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "dialogs/ui/dialogs_layout.h"
|
||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||
#include "dialogs/dialogs_widget.h"
|
||||
#include "dialogs/dialogs_search_from_controllers.h"
|
||||
#include "history/history.h"
|
||||
|
@ -218,9 +219,21 @@ InnerWidget::InnerWidget(
|
|||
UpdateFlag::Name
|
||||
| UpdateFlag::Photo
|
||||
| UpdateFlag::IsContact
|
||||
| UpdateFlag::FullInfo
|
||||
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||
if (update.flags & (UpdateFlag::Name | UpdateFlag::Photo)) {
|
||||
this->update();
|
||||
if (update.flags
|
||||
& (UpdateFlag::Name
|
||||
| UpdateFlag::Photo
|
||||
| UpdateFlag::FullInfo)) {
|
||||
const auto peer = update.peer;
|
||||
const auto history = peer->owner().historyLoaded(peer);
|
||||
if (_state == WidgetState::Default) {
|
||||
if (history) {
|
||||
updateDialogRow({ history, FullMsgId() });
|
||||
}
|
||||
} else {
|
||||
this->update();
|
||||
}
|
||||
_updated.fire({});
|
||||
}
|
||||
if (update.flags & UpdateFlag::IsContact) {
|
||||
|
@ -425,11 +438,13 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
if (xadd || yadd) {
|
||||
p.translate(xadd, yadd);
|
||||
}
|
||||
const auto isActive = (row->key() == active);
|
||||
const auto isSelected = (row->key() == selected);
|
||||
const auto key = row->key();
|
||||
const auto isActive = (key == active);
|
||||
const auto isSelected = (key == selected);
|
||||
Ui::RowPainter::paint(
|
||||
p,
|
||||
row,
|
||||
validateVideoUserpic(row),
|
||||
_filterId,
|
||||
fullWidth,
|
||||
isActive,
|
||||
|
@ -548,6 +563,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
Ui::RowPainter::paint(
|
||||
p,
|
||||
_filterResults[from],
|
||||
validateVideoUserpic(row),
|
||||
_filterId,
|
||||
fullWidth,
|
||||
active,
|
||||
|
@ -652,6 +668,34 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
}
|
||||
|
||||
Ui::VideoUserpic *InnerWidget::validateVideoUserpic(not_null<Row*> row) {
|
||||
const auto history = row->history();
|
||||
return history ? validateVideoUserpic(history) : nullptr;
|
||||
}
|
||||
|
||||
Ui::VideoUserpic *InnerWidget::validateVideoUserpic(
|
||||
not_null<History*> history) {
|
||||
const auto peer = history->peer;
|
||||
if (!peer->isPremium()
|
||||
|| peer->userpicPhotoUnknown()
|
||||
|| !peer->userpicHasVideo()) {
|
||||
_videoUserpics.remove(peer);
|
||||
return nullptr;
|
||||
}
|
||||
const auto i = _videoUserpics.find(peer);
|
||||
if (i != end(_videoUserpics)) {
|
||||
return i->second.get();
|
||||
}
|
||||
const auto update = [=] {
|
||||
updateDialogRow({ history, FullMsgId() });
|
||||
updateSearchResult(history->peer);
|
||||
};
|
||||
return _videoUserpics.emplace(peer, std::make_unique<Ui::VideoUserpic>(
|
||||
peer,
|
||||
update
|
||||
)).first->second.get();
|
||||
}
|
||||
|
||||
void InnerWidget::paintCollapsedRows(Painter &p, QRect clip) const {
|
||||
auto index = 0;
|
||||
const auto rowHeight = st::dialogsImportantBarHeight;
|
||||
|
@ -1528,15 +1572,18 @@ void InnerWidget::refreshDialogRow(RowDescriptor row) {
|
|||
|
||||
void InnerWidget::updateSearchResult(not_null<PeerData*> peer) {
|
||||
if (_state == WidgetState::Filtered) {
|
||||
if (!_peerSearchResults.empty()) {
|
||||
auto index = 0, add = peerSearchOffset();
|
||||
for (const auto &result : _peerSearchResults) {
|
||||
if (result->peer == peer) {
|
||||
rtlupdate(0, add + index * st::dialogsRowHeight, width(), st::dialogsRowHeight);
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
const auto i = ranges::find(
|
||||
_peerSearchResults,
|
||||
peer,
|
||||
&PeerSearchResult::peer);
|
||||
if (i != end(_peerSearchResults)) {
|
||||
const auto top = peerSearchOffset();
|
||||
const auto index = (i - begin(_peerSearchResults));
|
||||
rtlupdate(
|
||||
0,
|
||||
top + index * st::dialogsRowHeight,
|
||||
width(),
|
||||
st::dialogsRowHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1968,11 +2015,13 @@ void InnerWidget::visibleTopBottomUpdated(
|
|||
_visibleTop = visibleTop;
|
||||
_visibleBottom = visibleBottom;
|
||||
loadPeerPhotos();
|
||||
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) >= height()) {
|
||||
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop)
|
||||
>= height()) {
|
||||
if (_loadMoreCallback) {
|
||||
_loadMoreCallback();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InnerWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
||||
|
|
|
@ -38,6 +38,11 @@ namespace Data {
|
|||
class CloudImageView;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs::Ui {
|
||||
using namespace ::Ui;
|
||||
class VideoUserpic;
|
||||
} // namespace Dialogs::Ui
|
||||
|
||||
namespace Dialogs {
|
||||
|
||||
class Row;
|
||||
|
@ -312,6 +317,9 @@ private:
|
|||
const Ui::Text::String &text) const;
|
||||
void refreshSearchInChatLabel();
|
||||
|
||||
Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
|
||||
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);
|
||||
|
||||
void clearSearchResults(bool clearPeerSearchResults = true);
|
||||
void updateSelectedRow(Key key = Key());
|
||||
|
||||
|
@ -411,6 +419,10 @@ private:
|
|||
Ui::Text::String _searchFromUserText;
|
||||
RowDescriptor _menuRow;
|
||||
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
std::unique_ptr<Ui::VideoUserpic>> _videoUserpics;
|
||||
|
||||
Fn<void()> _loadMoreCallback;
|
||||
rpl::event_stream<> _listBottomReached;
|
||||
rpl::event_stream<ChosenRow> _chosenRow;
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "history/history.h"
|
||||
|
@ -79,37 +80,27 @@ namespace {
|
|||
: accumulated;
|
||||
}
|
||||
|
||||
void PaintUserpic(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size) {
|
||||
if (videoUserpic) {
|
||||
videoUserpic->paintLeft(p, view, x, y, outerWidth, size);
|
||||
} else {
|
||||
peer->paintUserpicLeft(p, view, x, y, outerWidth, size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BasicRow::BasicRow() = default;
|
||||
BasicRow::~BasicRow() = default;
|
||||
|
||||
void BasicRow::setCornerBadgeShown(
|
||||
bool shown,
|
||||
Fn<void()> updateCallback) const {
|
||||
if (_cornerBadgeShown == shown) {
|
||||
return;
|
||||
}
|
||||
_cornerBadgeShown = shown;
|
||||
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
|
||||
_cornerBadgeUserpic->animation.change(
|
||||
_cornerBadgeShown ? 1. : 0.,
|
||||
st::dialogsOnlineBadgeDuration);
|
||||
} else if (updateCallback) {
|
||||
ensureCornerBadgeUserpic();
|
||||
_cornerBadgeUserpic->animation.start(
|
||||
std::move(updateCallback),
|
||||
_cornerBadgeShown ? 0. : 1.,
|
||||
_cornerBadgeShown ? 1. : 0.,
|
||||
st::dialogsOnlineBadgeDuration);
|
||||
}
|
||||
if (!_cornerBadgeShown
|
||||
&& _cornerBadgeUserpic
|
||||
&& !_cornerBadgeUserpic->animation.animating()) {
|
||||
_cornerBadgeUserpic = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BasicRow::addRipple(
|
||||
QPoint origin,
|
||||
QSize size,
|
||||
|
@ -144,130 +135,23 @@ void BasicRow::paintRipple(
|
|||
}
|
||||
}
|
||||
|
||||
void BasicRow::updateCornerBadgeShown(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void()> updateCallback) const {
|
||||
const auto shown = [&] {
|
||||
if (const auto user = peer->asUser()) {
|
||||
return Data::IsUserOnline(user);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return Data::ChannelHasActiveCall(channel);
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
setCornerBadgeShown(shown, std::move(updateCallback));
|
||||
}
|
||||
|
||||
void BasicRow::ensureCornerBadgeUserpic() const {
|
||||
if (_cornerBadgeUserpic) {
|
||||
return;
|
||||
}
|
||||
_cornerBadgeUserpic = std::make_unique<CornerBadgeUserpic>();
|
||||
}
|
||||
|
||||
void BasicRow::PaintCornerBadgeFrame(
|
||||
not_null<CornerBadgeUserpic*> data,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Data::CloudImageView> &view) {
|
||||
data->frame.fill(Qt::transparent);
|
||||
|
||||
Painter q(&data->frame);
|
||||
peer->paintUserpic(
|
||||
q,
|
||||
view,
|
||||
0,
|
||||
0,
|
||||
st::dialogsPhotoSize);
|
||||
|
||||
PainterHighQualityEnabler hq(q);
|
||||
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
|
||||
const auto size = peer->isUser()
|
||||
? st::dialogsOnlineBadgeSize
|
||||
: st::dialogsCallBadgeSize;
|
||||
const auto stroke = st::dialogsOnlineBadgeStroke;
|
||||
const auto skip = peer->isUser()
|
||||
? st::dialogsOnlineBadgeSkip
|
||||
: st::dialogsCallBadgeSkip;
|
||||
const auto shrink = (size / 2) * (1. - data->shown);
|
||||
|
||||
auto pen = QPen(Qt::transparent);
|
||||
pen.setWidthF(stroke * data->shown);
|
||||
q.setPen(pen);
|
||||
q.setBrush(data->active
|
||||
? st::dialogsOnlineBadgeFgActive
|
||||
: st::dialogsOnlineBadgeFg);
|
||||
q.drawEllipse(QRectF(
|
||||
st::dialogsPhotoSize - skip.x() - size,
|
||||
st::dialogsPhotoSize - skip.y() - size,
|
||||
size,
|
||||
size
|
||||
).marginsRemoved({ shrink, shrink, shrink, shrink }));
|
||||
}
|
||||
|
||||
void BasicRow::paintUserpic(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
History *historyForCornerBadge,
|
||||
crl::time now,
|
||||
bool active,
|
||||
int fullWidth) const {
|
||||
updateCornerBadgeShown(peer);
|
||||
|
||||
const auto shown = _cornerBadgeUserpic
|
||||
? _cornerBadgeUserpic->animation.value(_cornerBadgeShown ? 1. : 0.)
|
||||
: (_cornerBadgeShown ? 1. : 0.);
|
||||
if (!historyForCornerBadge || shown == 0.) {
|
||||
peer->paintUserpicLeft(
|
||||
p,
|
||||
_userpic,
|
||||
st::dialogsPadding.x(),
|
||||
st::dialogsPadding.y(),
|
||||
fullWidth,
|
||||
st::dialogsPhotoSize);
|
||||
if (!historyForCornerBadge || !_cornerBadgeShown) {
|
||||
_cornerBadgeUserpic = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
ensureCornerBadgeUserpic();
|
||||
if (_cornerBadgeUserpic->frame.isNull()) {
|
||||
_cornerBadgeUserpic->frame = QImage(
|
||||
st::dialogsPhotoSize * cRetinaFactor(),
|
||||
st::dialogsPhotoSize * cRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_cornerBadgeUserpic->frame.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
const auto key = peer->userpicUniqueKey(_userpic);
|
||||
if (_cornerBadgeUserpic->shown != shown
|
||||
|| _cornerBadgeUserpic->key != key
|
||||
|| _cornerBadgeUserpic->active != active) {
|
||||
_cornerBadgeUserpic->shown = shown;
|
||||
_cornerBadgeUserpic->key = key;
|
||||
_cornerBadgeUserpic->active = active;
|
||||
PaintCornerBadgeFrame(_cornerBadgeUserpic.get(), peer, _userpic);
|
||||
}
|
||||
p.drawImage(st::dialogsPadding, _cornerBadgeUserpic->frame);
|
||||
if (historyForCornerBadge->peer->isUser()) {
|
||||
return;
|
||||
}
|
||||
const auto actionPainter = historyForCornerBadge->sendActionPainter();
|
||||
const auto bg = active
|
||||
? st::dialogsBgActive
|
||||
: st::dialogsBg;
|
||||
const auto size = st::dialogsCallBadgeSize;
|
||||
const auto skip = st::dialogsCallBadgeSkip;
|
||||
p.setOpacity(shown);
|
||||
p.translate(st::dialogsPadding);
|
||||
actionPainter->paintSpeaking(
|
||||
PaintUserpic(
|
||||
p,
|
||||
st::dialogsPhotoSize - skip.x() - size,
|
||||
st::dialogsPhotoSize - skip.y() - size,
|
||||
peer,
|
||||
videoUserpic,
|
||||
_userpic,
|
||||
st::dialogsPadding.x(),
|
||||
st::dialogsPadding.y(),
|
||||
fullWidth,
|
||||
bg,
|
||||
now);
|
||||
p.translate(-st::dialogsPadding);
|
||||
p.setOpacity(1.);
|
||||
st::dialogsPhotoSize);
|
||||
}
|
||||
|
||||
Row::Row(Key key, int pos) : _id(key), _pos(pos) {
|
||||
|
@ -297,6 +181,172 @@ void Row::validateListEntryCache() const {
|
|||
Ui::ItemTextDefaultOptions());
|
||||
}
|
||||
|
||||
void Row::setCornerBadgeShown(
|
||||
bool shown,
|
||||
Fn<void()> updateCallback) const {
|
||||
if (_cornerBadgeShown == shown) {
|
||||
return;
|
||||
}
|
||||
_cornerBadgeShown = shown;
|
||||
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
|
||||
_cornerBadgeUserpic->animation.change(
|
||||
_cornerBadgeShown ? 1. : 0.,
|
||||
st::dialogsOnlineBadgeDuration);
|
||||
} else if (updateCallback) {
|
||||
ensureCornerBadgeUserpic();
|
||||
_cornerBadgeUserpic->animation.start(
|
||||
std::move(updateCallback),
|
||||
_cornerBadgeShown ? 0. : 1.,
|
||||
_cornerBadgeShown ? 1. : 0.,
|
||||
st::dialogsOnlineBadgeDuration);
|
||||
}
|
||||
if (!_cornerBadgeShown
|
||||
&& _cornerBadgeUserpic
|
||||
&& !_cornerBadgeUserpic->animation.animating()) {
|
||||
_cornerBadgeUserpic = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Row::updateCornerBadgeShown(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void()> updateCallback) const {
|
||||
const auto shown = [&] {
|
||||
if (const auto user = peer->asUser()) {
|
||||
return Data::IsUserOnline(user);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return Data::ChannelHasActiveCall(channel);
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
setCornerBadgeShown(shown, std::move(updateCallback));
|
||||
}
|
||||
|
||||
void Row::ensureCornerBadgeUserpic() const {
|
||||
if (_cornerBadgeUserpic) {
|
||||
return;
|
||||
}
|
||||
_cornerBadgeUserpic = std::make_unique<CornerBadgeUserpic>();
|
||||
}
|
||||
|
||||
void Row::PaintCornerBadgeFrame(
|
||||
not_null<CornerBadgeUserpic*> data,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
std::shared_ptr<Data::CloudImageView> &view) {
|
||||
data->frame.fill(Qt::transparent);
|
||||
|
||||
Painter q(&data->frame);
|
||||
PaintUserpic(
|
||||
q,
|
||||
peer,
|
||||
videoUserpic,
|
||||
view,
|
||||
0,
|
||||
0,
|
||||
data->frame.width() / data->frame.devicePixelRatio(),
|
||||
st::dialogsPhotoSize);
|
||||
|
||||
PainterHighQualityEnabler hq(q);
|
||||
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
|
||||
const auto size = peer->isUser()
|
||||
? st::dialogsOnlineBadgeSize
|
||||
: st::dialogsCallBadgeSize;
|
||||
const auto stroke = st::dialogsOnlineBadgeStroke;
|
||||
const auto skip = peer->isUser()
|
||||
? st::dialogsOnlineBadgeSkip
|
||||
: st::dialogsCallBadgeSkip;
|
||||
const auto shrink = (size / 2) * (1. - data->shown);
|
||||
|
||||
auto pen = QPen(Qt::transparent);
|
||||
pen.setWidthF(stroke * data->shown);
|
||||
q.setPen(pen);
|
||||
q.setBrush(data->active
|
||||
? st::dialogsOnlineBadgeFgActive
|
||||
: st::dialogsOnlineBadgeFg);
|
||||
q.drawEllipse(QRectF(
|
||||
st::dialogsPhotoSize - skip.x() - size,
|
||||
st::dialogsPhotoSize - skip.y() - size,
|
||||
size,
|
||||
size
|
||||
).marginsRemoved({ shrink, shrink, shrink, shrink }));
|
||||
}
|
||||
|
||||
void Row::paintUserpic(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
History *historyForCornerBadge,
|
||||
crl::time now,
|
||||
bool active,
|
||||
int fullWidth) const {
|
||||
updateCornerBadgeShown(peer);
|
||||
|
||||
const auto shown = _cornerBadgeUserpic
|
||||
? _cornerBadgeUserpic->animation.value(_cornerBadgeShown ? 1. : 0.)
|
||||
: (_cornerBadgeShown ? 1. : 0.);
|
||||
if (!historyForCornerBadge || shown == 0.) {
|
||||
BasicRow::paintUserpic(
|
||||
p,
|
||||
peer,
|
||||
videoUserpic,
|
||||
historyForCornerBadge,
|
||||
now,
|
||||
active,
|
||||
fullWidth);
|
||||
if (!historyForCornerBadge || !_cornerBadgeShown) {
|
||||
_cornerBadgeUserpic = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
ensureCornerBadgeUserpic();
|
||||
if (_cornerBadgeUserpic->frame.isNull()) {
|
||||
_cornerBadgeUserpic->frame = QImage(
|
||||
st::dialogsPhotoSize * cRetinaFactor(),
|
||||
st::dialogsPhotoSize * cRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_cornerBadgeUserpic->frame.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
const auto key = peer->userpicUniqueKey(userpicView());
|
||||
const auto frameIndex = videoUserpic ? videoUserpic->frameIndex() : -1;
|
||||
if (_cornerBadgeUserpic->shown != shown
|
||||
|| _cornerBadgeUserpic->key != key
|
||||
|| _cornerBadgeUserpic->active != active
|
||||
|| _cornerBadgeUserpic->frameIndex != frameIndex
|
||||
|| videoUserpic) {
|
||||
_cornerBadgeUserpic->shown = shown;
|
||||
_cornerBadgeUserpic->key = key;
|
||||
_cornerBadgeUserpic->active = active;
|
||||
_cornerBadgeUserpic->frameIndex = frameIndex;
|
||||
PaintCornerBadgeFrame(
|
||||
_cornerBadgeUserpic.get(),
|
||||
peer,
|
||||
videoUserpic,
|
||||
userpicView());
|
||||
}
|
||||
p.drawImage(st::dialogsPadding, _cornerBadgeUserpic->frame);
|
||||
if (historyForCornerBadge->peer->isUser()) {
|
||||
return;
|
||||
}
|
||||
const auto actionPainter = historyForCornerBadge->sendActionPainter();
|
||||
const auto bg = active
|
||||
? st::dialogsBgActive
|
||||
: st::dialogsBg;
|
||||
const auto size = st::dialogsCallBadgeSize;
|
||||
const auto skip = st::dialogsCallBadgeSkip;
|
||||
p.setOpacity(shown);
|
||||
p.translate(st::dialogsPadding);
|
||||
actionPainter->paintSpeaking(
|
||||
p,
|
||||
st::dialogsPhotoSize - skip.x() - size,
|
||||
st::dialogsPhotoSize - skip.y() - size,
|
||||
fullWidth,
|
||||
bg,
|
||||
now);
|
||||
p.translate(-st::dialogsPadding);
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
FakeRow::FakeRow(Key searchInChat, not_null<HistoryItem*> item)
|
||||
: _searchInChat(searchInChat)
|
||||
, _item(item) {
|
||||
|
|
|
@ -26,6 +26,7 @@ class RippleAnimation;
|
|||
namespace Dialogs::Ui {
|
||||
using namespace ::Ui;
|
||||
class RowPainter;
|
||||
class VideoUserpic;
|
||||
} // namespace Dialogs::Ui
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -35,14 +36,12 @@ enum class SortMode;
|
|||
class BasicRow {
|
||||
public:
|
||||
BasicRow();
|
||||
~BasicRow();
|
||||
virtual ~BasicRow();
|
||||
|
||||
void updateCornerBadgeShown(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void()> updateCallback = nullptr) const;
|
||||
void paintUserpic(
|
||||
virtual void paintUserpic(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
History *historyForCornerBadge,
|
||||
crl::time now,
|
||||
bool active,
|
||||
|
@ -63,27 +62,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
struct CornerBadgeUserpic {
|
||||
InMemoryKey key;
|
||||
float64 shown = 0.;
|
||||
bool active = false;
|
||||
QImage frame;
|
||||
Ui::Animations::Simple animation;
|
||||
};
|
||||
|
||||
void setCornerBadgeShown(
|
||||
bool shown,
|
||||
Fn<void()> updateCallback) const;
|
||||
void ensureCornerBadgeUserpic() const;
|
||||
static void PaintCornerBadgeFrame(
|
||||
not_null<CornerBadgeUserpic*> data,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Data::CloudImageView> &view);
|
||||
|
||||
mutable std::shared_ptr<Data::CloudImageView> _userpic;
|
||||
mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
|
||||
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
|
||||
mutable bool _cornerBadgeShown = false;
|
||||
|
||||
};
|
||||
|
||||
|
@ -94,6 +74,18 @@ public:
|
|||
}
|
||||
Row(Key key, int pos);
|
||||
|
||||
void updateCornerBadgeShown(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void()> updateCallback = nullptr) const;
|
||||
void paintUserpic(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
History *historyForCornerBadge,
|
||||
crl::time now,
|
||||
bool active,
|
||||
int fullWidth) const final override;
|
||||
|
||||
[[nodiscard]] Key key() const {
|
||||
return _id;
|
||||
}
|
||||
|
@ -122,10 +114,31 @@ public:
|
|||
private:
|
||||
friend class List;
|
||||
|
||||
struct CornerBadgeUserpic {
|
||||
InMemoryKey key;
|
||||
float64 shown = 0.;
|
||||
int frameIndex = -1;
|
||||
bool active = false;
|
||||
QImage frame;
|
||||
Ui::Animations::Simple animation;
|
||||
};
|
||||
|
||||
void setCornerBadgeShown(
|
||||
bool shown,
|
||||
Fn<void()> updateCallback) const;
|
||||
void ensureCornerBadgeUserpic() const;
|
||||
static void PaintCornerBadgeFrame(
|
||||
not_null<CornerBadgeUserpic*> data,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::VideoUserpic *videoUserpic,
|
||||
std::shared_ptr<Data::CloudImageView> &view);
|
||||
|
||||
Key _id;
|
||||
int _pos = 0;
|
||||
mutable uint32 _listEntryCacheVersion = 0;
|
||||
mutable Ui::Text::String _listEntryCache;
|
||||
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
|
||||
mutable bool _cornerBadgeShown = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_drafts.h"
|
||||
#include "data/data_session.h"
|
||||
#include "dialogs/dialogs_list.h"
|
||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "storage/localstorage.h"
|
||||
|
@ -310,6 +311,7 @@ void paintRow(
|
|||
not_null<const BasicRow*> row,
|
||||
not_null<Entry*> entry,
|
||||
Dialogs::Key chat,
|
||||
VideoUserpic *videoUserpic,
|
||||
FilterId filterId,
|
||||
PeerData *from,
|
||||
const HiddenSenderInfo *hiddenSenderInfo,
|
||||
|
@ -360,6 +362,7 @@ void paintRow(
|
|||
row->paintUserpic(
|
||||
p,
|
||||
from,
|
||||
videoUserpic,
|
||||
(flags & Flag::AllowUserOnline) ? history : nullptr,
|
||||
ms,
|
||||
active,
|
||||
|
@ -450,13 +453,13 @@ void paintRow(
|
|||
if (!ShowSendActionInDialogs(history)
|
||||
|| !history->sendActionPainter()->paint(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||
if (history->cloudDraftTextCache.isEmpty()) {
|
||||
auto draftWrapped = Ui::Text::PlainLink(
|
||||
auto draftWrapped = Text::PlainLink(
|
||||
tr::lng_dialogs_text_from_wrapped(
|
||||
tr::now,
|
||||
lt_from,
|
||||
tr::lng_from_draft(tr::now)));
|
||||
auto draftText = supportMode
|
||||
? Ui::Text::PlainLink(
|
||||
? Text::PlainLink(
|
||||
Support::ChatOccupiedString(history))
|
||||
: tr::lng_dialogs_text_with_from(
|
||||
tr::now,
|
||||
|
@ -464,7 +467,7 @@ void paintRow(
|
|||
draftWrapped,
|
||||
lt_message,
|
||||
{ .text = draft->textWithTags.text },
|
||||
Ui::Text::WithEntities);
|
||||
Text::WithEntities);
|
||||
history->cloudDraftTextCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
draftText,
|
||||
|
@ -790,6 +793,7 @@ QRect PaintUnreadBadge(
|
|||
void RowPainter::paint(
|
||||
Painter &p,
|
||||
not_null<const Row*> row,
|
||||
VideoUserpic *videoUserpic,
|
||||
FilterId filterId,
|
||||
int fullWidth,
|
||||
bool active,
|
||||
|
@ -934,6 +938,7 @@ void RowPainter::paint(
|
|||
row,
|
||||
entry,
|
||||
row->key(),
|
||||
videoUserpic,
|
||||
filterId,
|
||||
from,
|
||||
nullptr,
|
||||
|
@ -1067,6 +1072,7 @@ void RowPainter::paint(
|
|||
row,
|
||||
history,
|
||||
history,
|
||||
nullptr,
|
||||
FilterId(),
|
||||
from,
|
||||
hiddenSenderInfo,
|
||||
|
|
|
@ -18,6 +18,8 @@ class BasicRow;
|
|||
|
||||
namespace Dialogs::Ui {
|
||||
|
||||
class VideoUserpic;
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
const style::icon *ChatTypeIcon(
|
||||
|
@ -30,6 +32,7 @@ public:
|
|||
static void paint(
|
||||
Painter &p,
|
||||
not_null<const Row*> row,
|
||||
VideoUserpic *videoUserpic,
|
||||
FilterId filterId,
|
||||
int fullWidth,
|
||||
bool active,
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||
|
||||
#include "core/file_location.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_session.h"
|
||||
|
||||
namespace Dialogs::Ui {
|
||||
|
||||
VideoUserpic::VideoUserpic(not_null<PeerData*> peer, Fn<void()> repaint)
|
||||
: _peer(peer)
|
||||
, _repaint(std::move(repaint)) {
|
||||
}
|
||||
|
||||
VideoUserpic::~VideoUserpic() = default;
|
||||
|
||||
int VideoUserpic::frameIndex() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void VideoUserpic::paintLeft(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int size) {
|
||||
_lastSize = size;
|
||||
|
||||
const auto photoId = _peer->userpicPhotoId();
|
||||
if (_videoPhotoId != photoId) {
|
||||
_videoPhotoId = photoId;
|
||||
_video = nullptr;
|
||||
_videoPhotoMedia = nullptr;
|
||||
const auto photo = _peer->owner().photo(photoId);
|
||||
if (photo->isNull()) {
|
||||
_peer->updateFullForced();
|
||||
} else {
|
||||
_videoPhotoMedia = photo->createMediaView();
|
||||
_videoPhotoMedia->videoWanted(_peer->userpicPhotoOrigin());
|
||||
}
|
||||
}
|
||||
if (!_video) {
|
||||
if (!_videoPhotoMedia) {
|
||||
const auto photo = _peer->owner().photo(photoId);
|
||||
if (!photo->isNull()) {
|
||||
_videoPhotoMedia = photo->createMediaView();
|
||||
_videoPhotoMedia->videoWanted(_peer->userpicPhotoOrigin());
|
||||
}
|
||||
}
|
||||
if (_videoPhotoMedia) {
|
||||
auto bytes = _videoPhotoMedia->videoContent();
|
||||
if (!bytes.isEmpty()) {
|
||||
auto callback = [=](Media::Clip::Notification notification) {
|
||||
clipCallback(notification);
|
||||
};
|
||||
_video = Media::Clip::MakeReader(
|
||||
Core::FileLocation(),
|
||||
std::move(bytes),
|
||||
std::move(callback));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rtl()) {
|
||||
x = w - x - size;
|
||||
}
|
||||
if (_video && _video->ready()) {
|
||||
startReady();
|
||||
|
||||
const auto now = crl::now();
|
||||
p.drawPixmap(
|
||||
x,
|
||||
y,
|
||||
_video->current(request(size), now));
|
||||
} else {
|
||||
_peer->paintUserpicLeft(p, view, x, y, w, size);
|
||||
}
|
||||
}
|
||||
|
||||
Media::Clip::FrameRequest VideoUserpic::request(int size) const {
|
||||
return {
|
||||
.frame = { size, size },
|
||||
.outer = { size, size },
|
||||
.factor = cIntRetinaFactor(),
|
||||
.radius = ImageRoundRadius::Ellipse,
|
||||
};
|
||||
}
|
||||
|
||||
bool VideoUserpic::startReady(int size) {
|
||||
if (!_video->ready() || _video->started()) {
|
||||
return false;
|
||||
} else if (!_lastSize) {
|
||||
_lastSize = size ? size : _video->width();
|
||||
}
|
||||
_video->start(request(_lastSize));
|
||||
_repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoUserpic::clipCallback(Media::Clip::Notification notification) {
|
||||
using namespace Media::Clip;
|
||||
|
||||
switch (notification) {
|
||||
case Notification::Reinit: {
|
||||
if (_video->state() == State::Error) {
|
||||
_video.setBad();
|
||||
} else if (startReady()) {
|
||||
_repaint();
|
||||
}
|
||||
} break;
|
||||
|
||||
case Notification::Repaint: _repaint(); break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Dialogs::Ui
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
|
||||
class Painter;
|
||||
|
||||
namespace Data {
|
||||
class CloudImageView;
|
||||
class PhotoMedia;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
class VideoUserpic final {
|
||||
public:
|
||||
VideoUserpic(not_null<PeerData*> peer, Fn<void()> repaint);
|
||||
~VideoUserpic();
|
||||
|
||||
[[nodiscard]] int frameIndex() const;
|
||||
|
||||
void paintLeft(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int size);
|
||||
|
||||
private:
|
||||
void clipCallback(Media::Clip::Notification notification);
|
||||
[[nodiscard]] Media::Clip::FrameRequest request(int size) const;
|
||||
bool startReady(int size = 0);
|
||||
|
||||
const not_null<PeerData*> _peer;
|
||||
const Fn<void()> _repaint;
|
||||
|
||||
Media::Clip::ReaderPointer _video;
|
||||
int _lastSize = 0;
|
||||
std::shared_ptr<Data::PhotoMedia> _videoPhotoMedia;
|
||||
PhotoId _videoPhotoId = 0;
|
||||
PhotoId _photoIdRequested = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Dialogs::Ui
|
|
@ -1470,7 +1470,7 @@ void InnerWidget::suggestRestrictParticipant(
|
|||
editRestrictions(false, ChatRestrictionsInfo());
|
||||
}).send();
|
||||
}
|
||||
});
|
||||
}, &st::menuIconRestrict);
|
||||
}
|
||||
|
||||
void InnerWidget::restrictParticipant(
|
||||
|
|
|
@ -1362,6 +1362,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
|||
const auto payment = Get<HistoryServicePayment>();
|
||||
const auto id = fullId();
|
||||
const auto owner = &history()->owner();
|
||||
payment->slug = data.vinvoice_slug().value_or_empty();
|
||||
payment->amount = Ui::FillAmountAndCurrency(amount, currency);
|
||||
payment->invoiceLink = std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
|
|
|
@ -153,10 +153,13 @@ void UploadPhoto(not_null<UserData*> user, QImage image) {
|
|||
auto bytes = QByteArray();
|
||||
auto buffer = QBuffer(&bytes);
|
||||
image.save(&buffer, "JPG", 87);
|
||||
user->setUserpic(base::RandomValue<PhotoId>(), ImageLocation(
|
||||
{ .data = InMemoryLocation{ .bytes = bytes } },
|
||||
image.width(),
|
||||
image.height()));
|
||||
user->setUserpic(
|
||||
base::RandomValue<PhotoId>(),
|
||||
ImageLocation(
|
||||
{ .data = InMemoryLocation{ .bytes = bytes } },
|
||||
image.width(),
|
||||
image.height()),
|
||||
false);
|
||||
user->session().api().peerPhoto().upload(user, std::move(image));
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,7 @@ uint32 peerSize(not_null<PeerData*> peer) {
|
|||
void writePeer(QDataStream &stream, not_null<PeerData*> peer) {
|
||||
stream << SerializePeerId(peer->id) << quint64(peer->userpicPhotoId());
|
||||
writeImageLocation(stream, peer->userpicLocation());
|
||||
stream << qint32(peer->userpicHasVideo() ? 1 : 0);
|
||||
if (const auto user = peer->asUser()) {
|
||||
const auto botInlinePlaceholder = user->isBot()
|
||||
? user->botInfo->inlinePlaceholder
|
||||
|
@ -190,6 +191,7 @@ PeerData *readPeer(
|
|||
int streamAppVersion,
|
||||
QDataStream &stream) {
|
||||
quint64 peerIdSerialized = 0, photoId = 0;
|
||||
qint32 photoHasVideo = 0;
|
||||
stream >> peerIdSerialized >> photoId;
|
||||
const auto peerId = DeserializePeerId(peerIdSerialized);
|
||||
if (!peerId) {
|
||||
|
@ -201,6 +203,9 @@ PeerData *readPeer(
|
|||
if (!userpic) {
|
||||
return nullptr;
|
||||
}
|
||||
if (streamAppVersion >= 4000000 || true AssertIsDebug()) {
|
||||
stream >> photoHasVideo;
|
||||
}
|
||||
|
||||
const auto selfId = session->userPeerId();
|
||||
const auto loaded = (peerId == selfId)
|
||||
|
@ -424,7 +429,7 @@ PeerData *readPeer(
|
|||
result->id.value,
|
||||
userpicAccessHash,
|
||||
photoId);
|
||||
result->setUserpic(photoId, location);
|
||||
result->setUserpic(photoId, location, (photoHasVideo == 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -439,6 +444,10 @@ QString peekUserPhone(int streamAppVersion, QDataStream &stream) {
|
|||
|| !readImageLocation(streamAppVersion, stream)) {
|
||||
return QString();
|
||||
}
|
||||
if (streamAppVersion >= 4000000 || true AssertIsDebug()) {
|
||||
qint32 photoHasVideo = 0;
|
||||
stream >> photoHasVideo;
|
||||
}
|
||||
|
||||
QString first, last, phone;
|
||||
stream >> first >> last >> phone;
|
||||
|
|
Loading…
Reference in New Issue