mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Show stories in chats list userpics.
This commit is contained in:
parent
9a29807276
commit
d7d8847c1d
11 changed files with 259 additions and 33 deletions
|
@ -2524,6 +2524,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
_session->data().stories().apply(update.c_updateStory());
|
_session->data().stories().apply(update.c_updateStory());
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updateReadStories: {
|
||||||
|
_session->data().stories().apply(update.c_updateReadStories());
|
||||||
|
} break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -525,6 +525,13 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
|
||||||
| Flag::DiscardMinPhoto
|
| Flag::DiscardMinPhoto
|
||||||
| Flag::StoriesHidden
|
| Flag::StoriesHidden
|
||||||
: Flag());
|
: Flag());
|
||||||
|
const auto storiesState = minimal
|
||||||
|
? std::optional<Data::Stories::PeerSourceState>()
|
||||||
|
: data.is_stories_unavailable()
|
||||||
|
? Data::Stories::PeerSourceState()
|
||||||
|
: !data.vstories_max_id()
|
||||||
|
? std::optional<Data::Stories::PeerSourceState>()
|
||||||
|
: stories().peerSourceState(result, data.vstories_max_id()->v);
|
||||||
const auto flagsSet = (data.is_deleted() ? Flag::Deleted : Flag())
|
const auto flagsSet = (data.is_deleted() ? Flag::Deleted : Flag())
|
||||||
| (data.is_verified() ? Flag::Verified : Flag())
|
| (data.is_verified() ? Flag::Verified : Flag())
|
||||||
| (data.is_scam() ? Flag::Scam : Flag())
|
| (data.is_scam() ? Flag::Scam : Flag())
|
||||||
|
@ -551,6 +558,13 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
|
||||||
MTP_long(data.vaccess_hash().value_or_empty()));
|
MTP_long(data.vaccess_hash().value_or_empty()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (storiesState) {
|
||||||
|
result->setStoriesState(!storiesState->maxId
|
||||||
|
? UserData::StoriesState::None
|
||||||
|
: (storiesState->maxId > storiesState->readTill)
|
||||||
|
? UserData::StoriesState::HasUnread
|
||||||
|
: UserData::StoriesState::HasRead);
|
||||||
|
}
|
||||||
if (data.is_self()) {
|
if (data.is_self()) {
|
||||||
result->input = MTP_inputPeerSelf();
|
result->input = MTP_inputPeerSelf();
|
||||||
result->inputUser = MTP_inputUserSelf();
|
result->inputUser = MTP_inputUserSelf();
|
||||||
|
|
|
@ -136,7 +136,7 @@ void Stories::apply(const MTPDupdateStory &data) {
|
||||||
i->second.ids.emplace(idDates);
|
i->second.ids.emplace(idDates);
|
||||||
const auto nowInfo = i->second.info();
|
const auto nowInfo = i->second.info();
|
||||||
if (user->isSelf() && i->second.readTill < idDates.id) {
|
if (user->isSelf() && i->second.readTill < idDates.id) {
|
||||||
i->second.readTill = idDates.id;
|
_readTill[user->id] = i->second.readTill = idDates.id;
|
||||||
}
|
}
|
||||||
if (wasInfo == nowInfo) {
|
if (wasInfo == nowInfo) {
|
||||||
return;
|
return;
|
||||||
|
@ -158,6 +158,11 @@ void Stories::apply(const MTPDupdateStory &data) {
|
||||||
refreshInList(StorySourcesList::NotHidden);
|
refreshInList(StorySourcesList::NotHidden);
|
||||||
}
|
}
|
||||||
_sourceChanged.fire_copy(peerId);
|
_sourceChanged.fire_copy(peerId);
|
||||||
|
updateUserStoriesState(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::apply(const MTPDupdateReadStories &data) {
|
||||||
|
bumpReadTill(peerFromUser(data.vuser_id()), data.vmax_id().v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::apply(not_null<PeerData*> peer, const MTPUserStories *data) {
|
void Stories::apply(not_null<PeerData*> peer, const MTPUserStories *data) {
|
||||||
|
@ -166,6 +171,7 @@ void Stories::apply(not_null<PeerData*> peer, const MTPUserStories *data) {
|
||||||
applyDeletedFromSources(peer->id, StorySourcesList::Hidden);
|
applyDeletedFromSources(peer->id, StorySourcesList::Hidden);
|
||||||
_all.erase(peer->id);
|
_all.erase(peer->id);
|
||||||
_sourceChanged.fire_copy(peer->id);
|
_sourceChanged.fire_copy(peer->id);
|
||||||
|
updateUserStoriesState(peer);
|
||||||
} else {
|
} else {
|
||||||
parseAndApply(*data);
|
parseAndApply(*data);
|
||||||
}
|
}
|
||||||
|
@ -281,6 +287,7 @@ void Stories::parseAndApply(const MTPUserStories &stories) {
|
||||||
} else if (user->isSelf()) {
|
} else if (user->isSelf()) {
|
||||||
result.readTill = result.ids.back().id;
|
result.readTill = result.ids.back().id;
|
||||||
}
|
}
|
||||||
|
_readTill[peerId] = result.readTill;
|
||||||
const auto info = result.info();
|
const auto info = result.info();
|
||||||
const auto i = _all.find(peerId);
|
const auto i = _all.find(peerId);
|
||||||
if (i != end(_all)) {
|
if (i != end(_all)) {
|
||||||
|
@ -317,6 +324,7 @@ void Stories::parseAndApply(const MTPUserStories &stories) {
|
||||||
applyDeletedFromSources(peerId, StorySourcesList::Hidden);
|
applyDeletedFromSources(peerId, StorySourcesList::Hidden);
|
||||||
}
|
}
|
||||||
_sourceChanged.fire_copy(peerId);
|
_sourceChanged.fire_copy(peerId);
|
||||||
|
updateUserStoriesState(result.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
Story *Stories::parseAndApply(
|
Story *Stories::parseAndApply(
|
||||||
|
@ -684,12 +692,14 @@ void Stories::applyRemovedFromActive(FullStoryId id) {
|
||||||
const auto j = i->second.ids.lower_bound(StoryIdDates{ id.story });
|
const auto j = i->second.ids.lower_bound(StoryIdDates{ id.story });
|
||||||
if (j != end(i->second.ids) && j->id == id.story) {
|
if (j != end(i->second.ids) && j->id == id.story) {
|
||||||
i->second.ids.erase(j);
|
i->second.ids.erase(j);
|
||||||
|
const auto user = i->second.user;
|
||||||
if (i->second.ids.empty()) {
|
if (i->second.ids.empty()) {
|
||||||
_all.erase(i);
|
_all.erase(i);
|
||||||
removeFromList(StorySourcesList::NotHidden);
|
removeFromList(StorySourcesList::NotHidden);
|
||||||
removeFromList(StorySourcesList::Hidden);
|
removeFromList(StorySourcesList::Hidden);
|
||||||
}
|
}
|
||||||
_sourceChanged.fire_copy(id.peer);
|
_sourceChanged.fire_copy(id.peer);
|
||||||
|
updateUserStoriesState(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -891,22 +901,35 @@ void Stories::markAsRead(FullStoryId id, bool viewed) {
|
||||||
_incrementViewsTimer.callOnce(kIncrementViewsDelay);
|
_incrementViewsTimer.callOnce(kIncrementViewsDelay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto i = _all.find(id.peer);
|
if (!bumpReadTill(id.peer, id.story)) {
|
||||||
if (i == end(_all) || i->second.readTill >= id.story) {
|
|
||||||
return;
|
return;
|
||||||
} else if (!_markReadPending.contains(id.peer)) {
|
}
|
||||||
|
if (!_markReadPending.contains(id.peer)) {
|
||||||
sendMarkAsReadRequests();
|
sendMarkAsReadRequests();
|
||||||
}
|
}
|
||||||
_markReadPending.emplace(id.peer);
|
_markReadPending.emplace(id.peer);
|
||||||
|
_markReadTimer.callOnce(kMarkAsReadDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stories::bumpReadTill(PeerId peerId, StoryId maxReadTill) {
|
||||||
|
auto &till = _readTill[peerId];
|
||||||
|
if (till < maxReadTill) {
|
||||||
|
till = maxReadTill;
|
||||||
|
updateUserStoriesState(_owner->peer(peerId));
|
||||||
|
}
|
||||||
|
const auto i = _all.find(peerId);
|
||||||
|
if (i == end(_all) || i->second.readTill >= maxReadTill) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const auto wasUnreadCount = i->second.unreadCount();
|
const auto wasUnreadCount = i->second.unreadCount();
|
||||||
i->second.readTill = id.story;
|
i->second.readTill = maxReadTill;
|
||||||
const auto nowUnreadCount = i->second.unreadCount();
|
const auto nowUnreadCount = i->second.unreadCount();
|
||||||
if (wasUnreadCount != nowUnreadCount) {
|
if (wasUnreadCount != nowUnreadCount) {
|
||||||
const auto refreshInList = [&](StorySourcesList list) {
|
const auto refreshInList = [&](StorySourcesList list) {
|
||||||
auto &sources = _sources[static_cast<int>(list)];
|
auto &sources = _sources[static_cast<int>(list)];
|
||||||
const auto i = ranges::find(
|
const auto i = ranges::find(
|
||||||
sources,
|
sources,
|
||||||
id.peer,
|
peerId,
|
||||||
&StoriesSourceInfo::id);
|
&StoriesSourceInfo::id);
|
||||||
if (i != end(sources)) {
|
if (i != end(sources)) {
|
||||||
i->unreadCount = nowUnreadCount;
|
i->unreadCount = nowUnreadCount;
|
||||||
|
@ -916,7 +939,7 @@ void Stories::markAsRead(FullStoryId id, bool viewed) {
|
||||||
refreshInList(StorySourcesList::NotHidden);
|
refreshInList(StorySourcesList::NotHidden);
|
||||||
refreshInList(StorySourcesList::Hidden);
|
refreshInList(StorySourcesList::Hidden);
|
||||||
}
|
}
|
||||||
_markReadTimer.callOnce(kMarkAsReadDelay);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::toggleHidden(
|
void Stories::toggleHidden(
|
||||||
|
@ -1408,6 +1431,53 @@ void Stories::setPreloadingInViewer(std::vector<FullStoryId> ids) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Stories::PeerSourceState> Stories::peerSourceState(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
StoryId storyMaxId) {
|
||||||
|
const auto i = _readTill.find(peer->id);
|
||||||
|
if (_readTillReceived || (i != end(_readTill))) {
|
||||||
|
return PeerSourceState{
|
||||||
|
.maxId = storyMaxId,
|
||||||
|
.readTill = std::min(
|
||||||
|
storyMaxId,
|
||||||
|
(i != end(_readTill)) ? i->second : 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!_readTillsRequestId) {
|
||||||
|
const auto api = &_owner->session().api();
|
||||||
|
_readTillsRequestId = api->request(MTPstories_GetAllReadUserStories(
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
_readTillReceived = true;
|
||||||
|
api->applyUpdates(result);
|
||||||
|
for (auto &[peer, maxId] : base::take(_pendingUserStateMaxId)) {
|
||||||
|
updateUserStoriesState(peer);
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
_pendingUserStateMaxId[peer] = storyMaxId;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::updateUserStoriesState(not_null<PeerData*> peer) {
|
||||||
|
const auto till = _readTill.find(peer->id);
|
||||||
|
const auto readTill = (till != end(_readTill)) ? till->second : 0;
|
||||||
|
const auto pendingMaxId = [&] {
|
||||||
|
const auto j = _pendingUserStateMaxId.find(peer);
|
||||||
|
return (j != end(_pendingUserStateMaxId)) ? j->second : 0;
|
||||||
|
};
|
||||||
|
const auto i = _all.find(peer->id);
|
||||||
|
const auto max = (i != end(_all))
|
||||||
|
? (i->second.ids.empty() ? 0 : i->second.ids.back().id)
|
||||||
|
: pendingMaxId();
|
||||||
|
if (const auto user = peer->asUser()) {
|
||||||
|
user->setStoriesState(!max
|
||||||
|
? UserData::StoriesState::None
|
||||||
|
: (max <= readTill)
|
||||||
|
? UserData::StoriesState::HasRead
|
||||||
|
: UserData::StoriesState::HasUnread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Stories::preloadSourcesChanged(StorySourcesList list) {
|
void Stories::preloadSourcesChanged(StorySourcesList list) {
|
||||||
if (rebuildPreloadSources(list)) {
|
if (rebuildPreloadSources(list)) {
|
||||||
continuePreloading();
|
continuePreloading();
|
||||||
|
|
|
@ -137,6 +137,7 @@ public:
|
||||||
|
|
||||||
void loadMore(StorySourcesList list);
|
void loadMore(StorySourcesList list);
|
||||||
void apply(const MTPDupdateStory &data);
|
void apply(const MTPDupdateStory &data);
|
||||||
|
void apply(const MTPDupdateReadStories &data);
|
||||||
void apply(not_null<PeerData*> peer, const MTPUserStories *data);
|
void apply(not_null<PeerData*> peer, const MTPUserStories *data);
|
||||||
Story *applyFromWebpage(PeerId peerId, const MTPstoryItem &story);
|
Story *applyFromWebpage(PeerId peerId, const MTPstoryItem &story);
|
||||||
void loadAround(FullStoryId id, StoriesContext context);
|
void loadAround(FullStoryId id, StoriesContext context);
|
||||||
|
@ -199,6 +200,14 @@ public:
|
||||||
void decrementPreloadingHiddenSources();
|
void decrementPreloadingHiddenSources();
|
||||||
void setPreloadingInViewer(std::vector<FullStoryId> ids);
|
void setPreloadingInViewer(std::vector<FullStoryId> ids);
|
||||||
|
|
||||||
|
struct PeerSourceState {
|
||||||
|
StoryId maxId = 0;
|
||||||
|
StoryId readTill = 0;
|
||||||
|
};
|
||||||
|
[[nodiscard]] std::optional<PeerSourceState> peerSourceState(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
StoryId storyMaxId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Saved {
|
struct Saved {
|
||||||
StoriesIds ids;
|
StoriesIds ids;
|
||||||
|
@ -222,6 +231,7 @@ private:
|
||||||
const QVector<MTPStoryItem> &list);
|
const QVector<MTPStoryItem> &list);
|
||||||
void sendResolveRequests();
|
void sendResolveRequests();
|
||||||
void finalizeResolve(FullStoryId id);
|
void finalizeResolve(FullStoryId id);
|
||||||
|
void updateUserStoriesState(not_null<PeerData*> peer);
|
||||||
|
|
||||||
void applyDeleted(FullStoryId id);
|
void applyDeleted(FullStoryId id);
|
||||||
void applyExpired(FullStoryId id);
|
void applyExpired(FullStoryId id);
|
||||||
|
@ -230,6 +240,7 @@ private:
|
||||||
void removeDependencyStory(not_null<Story*> story);
|
void removeDependencyStory(not_null<Story*> story);
|
||||||
void savedStateUpdated(not_null<Story*> story);
|
void savedStateUpdated(not_null<Story*> story);
|
||||||
void sort(StorySourcesList list);
|
void sort(StorySourcesList list);
|
||||||
|
bool bumpReadTill(PeerId peerId, StoryId maxReadTill);
|
||||||
|
|
||||||
[[nodiscard]] std::shared_ptr<HistoryItem> lookupItem(
|
[[nodiscard]] std::shared_ptr<HistoryItem> lookupItem(
|
||||||
not_null<Story*> story);
|
not_null<Story*> story);
|
||||||
|
@ -317,6 +328,11 @@ private:
|
||||||
int _preloadingHiddenSourcesCounter = 0;
|
int _preloadingHiddenSourcesCounter = 0;
|
||||||
int _preloadingMainSourcesCounter = 0;
|
int _preloadingMainSourcesCounter = 0;
|
||||||
|
|
||||||
|
base::flat_map<PeerId, StoryId> _readTill;
|
||||||
|
base::flat_map<not_null<PeerData*>, StoryId> _pendingUserStateMaxId;
|
||||||
|
mtpRequestId _readTillsRequestId = 0;
|
||||||
|
bool _readTillReceived = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_user_names.h"
|
#include "data/data_user_names.h"
|
||||||
#include "data/data_wall_paper.h"
|
#include "data/data_wall_paper.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "api/api_peer_photo.h"
|
#include "api/api_peer_photo.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
|
@ -125,6 +126,38 @@ void UserData::setPrivateForwardName(const QString &name) {
|
||||||
_privateForwardName = name;
|
_privateForwardName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UserData::hasActiveStories() const {
|
||||||
|
return flags() & UserDataFlag::HasActiveStories;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserData::hasUnreadStories() const {
|
||||||
|
return flags() & UserDataFlag::HasUnreadStories;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserData::setStoriesState(StoriesState state) {
|
||||||
|
Expects(state != StoriesState::Unknown);
|
||||||
|
|
||||||
|
const auto was = flags();
|
||||||
|
using Flag = UserDataFlag;
|
||||||
|
switch (state) {
|
||||||
|
case StoriesState::None:
|
||||||
|
_flags.remove(Flag::HasActiveStories | Flag::HasUnreadStories);
|
||||||
|
break;
|
||||||
|
case StoriesState::HasRead:
|
||||||
|
_flags.set(
|
||||||
|
(flags() & ~Flag::HasUnreadStories) | Flag::HasActiveStories);
|
||||||
|
break;
|
||||||
|
case StoriesState::HasUnread:
|
||||||
|
_flags.add(Flag::HasActiveStories | Flag::HasUnreadStories);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (flags() != was) {
|
||||||
|
if (const auto history = owner().historyLoaded(this)) {
|
||||||
|
history->updateChatListEntryPostponed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
|
void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
|
||||||
bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty();
|
bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty();
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@ enum class UserDataFlag {
|
||||||
VoiceMessagesForbidden = (1 << 16),
|
VoiceMessagesForbidden = (1 << 16),
|
||||||
PersonalPhoto = (1 << 17),
|
PersonalPhoto = (1 << 17),
|
||||||
StoriesHidden = (1 << 18),
|
StoriesHidden = (1 << 18),
|
||||||
|
HasActiveStories = (1 << 19),
|
||||||
|
HasUnreadStories = (1 << 20),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(UserDataFlag) { return true; };
|
inline constexpr bool is_flag_type(UserDataFlag) { return true; };
|
||||||
using UserDataFlags = base::flags<UserDataFlag>;
|
using UserDataFlags = base::flags<UserDataFlag>;
|
||||||
|
@ -174,6 +176,16 @@ public:
|
||||||
[[nodiscard]] QString privateForwardName() const;
|
[[nodiscard]] QString privateForwardName() const;
|
||||||
void setPrivateForwardName(const QString &name);
|
void setPrivateForwardName(const QString &name);
|
||||||
|
|
||||||
|
enum class StoriesState {
|
||||||
|
Unknown,
|
||||||
|
None,
|
||||||
|
HasRead,
|
||||||
|
HasUnread,
|
||||||
|
};
|
||||||
|
[[nodiscard]] bool hasActiveStories() const;
|
||||||
|
[[nodiscard]] bool hasUnreadStories() const;
|
||||||
|
void setStoriesState(StoriesState state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto unavailableReasons() const
|
auto unavailableReasons() const
|
||||||
-> const std::vector<Data::UnavailableReason> & override;
|
-> const std::vector<Data::UnavailableReason> & override;
|
||||||
|
|
|
@ -1294,6 +1294,7 @@ void InnerWidget::selectByMouse(QPoint globalPosition) {
|
||||||
}
|
}
|
||||||
_mouseSelection = true;
|
_mouseSelection = true;
|
||||||
_lastMousePosition = globalPosition;
|
_lastMousePosition = globalPosition;
|
||||||
|
_lastRowLocalMouseX = local.x();
|
||||||
|
|
||||||
const auto w = width();
|
const auto w = width();
|
||||||
const auto mouseY = local.y();
|
const auto mouseY = local.y();
|
||||||
|
@ -2209,6 +2210,7 @@ FilterId InnerWidget::filterId() const {
|
||||||
void InnerWidget::clearSelection() {
|
void InnerWidget::clearSelection() {
|
||||||
_mouseSelection = false;
|
_mouseSelection = false;
|
||||||
_lastMousePosition = std::nullopt;
|
_lastMousePosition = std::nullopt;
|
||||||
|
_lastRowLocalMouseX = -1;
|
||||||
if (isSelected()) {
|
if (isSelected()) {
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
_collapsedSelected = -1;
|
_collapsedSelected = -1;
|
||||||
|
@ -2907,6 +2909,7 @@ void InnerWidget::resizeEmptyLabel() {
|
||||||
void InnerWidget::clearMouseSelection(bool clearSelection) {
|
void InnerWidget::clearMouseSelection(bool clearSelection) {
|
||||||
_mouseSelection = false;
|
_mouseSelection = false;
|
||||||
_lastMousePosition = std::nullopt;
|
_lastMousePosition = std::nullopt;
|
||||||
|
_lastRowLocalMouseX = -1;
|
||||||
if (clearSelection) {
|
if (clearSelection) {
|
||||||
if (_state == WidgetState::Default) {
|
if (_state == WidgetState::Default) {
|
||||||
_collapsedSelected = -1;
|
_collapsedSelected = -1;
|
||||||
|
@ -3375,29 +3378,30 @@ ChosenRow InnerWidget::computeChosenRow() const {
|
||||||
if (_state == WidgetState::Default) {
|
if (_state == WidgetState::Default) {
|
||||||
if (_selected) {
|
if (_selected) {
|
||||||
return {
|
return {
|
||||||
_selected->key(),
|
.key = _selected->key(),
|
||||||
Data::UnreadMessagePosition
|
.message = Data::UnreadMessagePosition,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (_state == WidgetState::Filtered) {
|
} else if (_state == WidgetState::Filtered) {
|
||||||
if (base::in_range(_filteredSelected, 0, _filterResults.size())) {
|
if (base::in_range(_filteredSelected, 0, _filterResults.size())) {
|
||||||
return {
|
return {
|
||||||
_filterResults[_filteredSelected].key(),
|
.key = _filterResults[_filteredSelected].key(),
|
||||||
Data::UnreadMessagePosition,
|
.message = Data::UnreadMessagePosition,
|
||||||
true
|
.filteredRow = true,
|
||||||
};
|
};
|
||||||
} else if (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())) {
|
} else if (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())) {
|
||||||
|
const auto peer = _peerSearchResults[_peerSearchSelected]->peer;
|
||||||
return {
|
return {
|
||||||
session().data().history(_peerSearchResults[_peerSearchSelected]->peer),
|
.key = session().data().history(peer),
|
||||||
Data::UnreadMessagePosition
|
.message = Data::UnreadMessagePosition
|
||||||
};
|
};
|
||||||
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
} else if (base::in_range(_searchedSelected, 0, _searchResults.size())) {
|
||||||
const auto result = _searchResults[_searchedSelected].get();
|
const auto result = _searchResults[_searchedSelected].get();
|
||||||
const auto topic = result->topic();
|
const auto topic = result->topic();
|
||||||
const auto item = result->item();
|
const auto item = result->item();
|
||||||
return {
|
return {
|
||||||
(topic ? (Entry*)topic : (Entry*)item->history()),
|
.key = (topic ? (Entry*)topic : (Entry*)item->history()),
|
||||||
item->position()
|
.message = item->position()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3413,10 +3417,12 @@ bool InnerWidget::chooseRow(
|
||||||
} else if (chooseHashtag()) {
|
} else if (chooseHashtag()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto modifyChosenRow = [](
|
const auto modifyChosenRow = [&](
|
||||||
ChosenRow row,
|
ChosenRow row,
|
||||||
Qt::KeyboardModifiers modifiers) {
|
Qt::KeyboardModifiers modifiers) {
|
||||||
row.newWindow = (modifiers & Qt::ControlModifier);
|
row.newWindow = (modifiers & Qt::ControlModifier);
|
||||||
|
row.userpicClick = (_lastRowLocalMouseX >= 0)
|
||||||
|
&& (_lastRowLocalMouseX < _st->nameLeft);
|
||||||
return row;
|
return row;
|
||||||
};
|
};
|
||||||
auto chosen = modifyChosenRow(computeChosenRow(), modifiers);
|
auto chosen = modifyChosenRow(computeChosenRow(), modifiers);
|
||||||
|
|
|
@ -65,8 +65,9 @@ class IndexedList;
|
||||||
struct ChosenRow {
|
struct ChosenRow {
|
||||||
Key key;
|
Key key;
|
||||||
Data::MessagePosition message;
|
Data::MessagePosition message;
|
||||||
bool filteredRow = false;
|
bool userpicClick : 1 = false;
|
||||||
bool newWindow = false;
|
bool filteredRow : 1 = false;
|
||||||
|
bool newWindow : 1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SearchRequestType {
|
enum class SearchRequestType {
|
||||||
|
@ -416,6 +417,7 @@ private:
|
||||||
FilterId _filterId = 0;
|
FilterId _filterId = 0;
|
||||||
bool _mouseSelection = false;
|
bool _mouseSelection = false;
|
||||||
std::optional<QPoint> _lastMousePosition;
|
std::optional<QPoint> _lastMousePosition;
|
||||||
|
int _lastRowLocalMouseX = -1;
|
||||||
Qt::MouseButton _pressButton = Qt::LeftButton;
|
Qt::MouseButton _pressButton = Qt::LeftButton;
|
||||||
|
|
||||||
Data::Folder *_openedFolder = nullptr;
|
Data::Folder *_openedFolder = nullptr;
|
||||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -330,6 +331,7 @@ void Row::ensureCornerBadgeUserpic() const {
|
||||||
|
|
||||||
void Row::PaintCornerBadgeFrame(
|
void Row::PaintCornerBadgeFrame(
|
||||||
not_null<CornerBadgeUserpic*> data,
|
not_null<CornerBadgeUserpic*> data,
|
||||||
|
int framePadding,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Ui::VideoUserpic *videoUserpic,
|
Ui::VideoUserpic *videoUserpic,
|
||||||
Ui::PeerUserpicView &view,
|
Ui::PeerUserpicView &view,
|
||||||
|
@ -337,6 +339,19 @@ void Row::PaintCornerBadgeFrame(
|
||||||
data->frame.fill(Qt::transparent);
|
data->frame.fill(Qt::transparent);
|
||||||
|
|
||||||
Painter q(&data->frame);
|
Painter q(&data->frame);
|
||||||
|
q.translate(framePadding, framePadding);
|
||||||
|
auto hq = std::optional<PainterHighQualityEnabler>();
|
||||||
|
if (data->storiesShown) {
|
||||||
|
hq.emplace(q);
|
||||||
|
const auto line = st::dialogsStoriesFull.lineTwice / 2.;
|
||||||
|
const auto skip = line * 3 / 2.;
|
||||||
|
const auto scale = 1. - (2 * skip / context.st->photoSize);
|
||||||
|
const auto center = context.st->photoSize / 2.;
|
||||||
|
q.save();
|
||||||
|
q.translate(center, center);
|
||||||
|
q.scale(scale, scale);
|
||||||
|
q.translate(-center, -center);
|
||||||
|
}
|
||||||
PaintUserpic(
|
PaintUserpic(
|
||||||
q,
|
q,
|
||||||
peer,
|
peer,
|
||||||
|
@ -347,9 +362,37 @@ void Row::PaintCornerBadgeFrame(
|
||||||
data->frame.width() / data->frame.devicePixelRatio(),
|
data->frame.width() / data->frame.devicePixelRatio(),
|
||||||
context.st->photoSize,
|
context.st->photoSize,
|
||||||
context.paused);
|
context.paused);
|
||||||
|
if (data->storiesShown) {
|
||||||
|
q.restore();
|
||||||
|
|
||||||
|
const auto st = context.st;
|
||||||
|
const auto storiesUnreadBrush = [&] {
|
||||||
|
const auto left = st->padding.left();
|
||||||
|
const auto top = st->padding.top();
|
||||||
|
auto gradient = QLinearGradient(
|
||||||
|
QPoint(left + st->photoSize, top),
|
||||||
|
QPoint(left, top + st->photoSize));
|
||||||
|
gradient.setStops({
|
||||||
|
{ 0., st::groupCallLive1->c },
|
||||||
|
{ 1., st::groupCallMuted1->c },
|
||||||
|
});
|
||||||
|
return QBrush(gradient);
|
||||||
|
};
|
||||||
|
const auto storiesBrush = data->storiesUnread
|
||||||
|
? storiesUnreadBrush()
|
||||||
|
: context.active
|
||||||
|
? st::dialogsUnreadBgMutedActive->b
|
||||||
|
: st::dialogsUnreadBgMuted->b;
|
||||||
|
const auto storiesLine = data->storiesUnread
|
||||||
|
? (st::dialogsStoriesFull.lineTwice / 2.)
|
||||||
|
: (st::dialogsStoriesFull.lineReadTwice / 2.);
|
||||||
|
const auto pen = QPen(storiesBrush, storiesLine);
|
||||||
|
q.setPen(pen);
|
||||||
|
q.drawEllipse(0, 0, st->photoSize, st->photoSize);
|
||||||
|
}
|
||||||
|
|
||||||
const auto &manager = data->layersManager;
|
const auto &manager = data->layersManager;
|
||||||
if (const auto p = manager.progressForLayer(kBottomLayer); p) {
|
if (const auto p = manager.progressForLayer(kBottomLayer); p > 0.) {
|
||||||
const auto size = context.st->photoSize;
|
const auto size = context.st->photoSize;
|
||||||
if (data->cacheTTL.isNull() && peer->messagesTTL()) {
|
if (data->cacheTTL.isNull() && peer->messagesTTL()) {
|
||||||
data->cacheTTL = CornerBadgeTTL(peer, view, size);
|
data->cacheTTL = CornerBadgeTTL(peer, view, size);
|
||||||
|
@ -364,7 +407,9 @@ void Row::PaintCornerBadgeFrame(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PainterHighQualityEnabler hq(q);
|
if (!hq) {
|
||||||
|
hq.emplace(q);
|
||||||
|
}
|
||||||
q.setCompositionMode(QPainter::CompositionMode_Source);
|
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
|
|
||||||
const auto size = peer->isUser()
|
const auto size = peer->isUser()
|
||||||
|
@ -401,7 +446,14 @@ void Row::paintUserpic(
|
||||||
const auto cornerBadgeShown = !_cornerBadgeUserpic
|
const auto cornerBadgeShown = !_cornerBadgeUserpic
|
||||||
? _cornerBadgeShown
|
? _cornerBadgeShown
|
||||||
: !_cornerBadgeUserpic->layersManager.isDisplayedNone();
|
: !_cornerBadgeUserpic->layersManager.isDisplayedNone();
|
||||||
if (!historyForCornerBadge || !cornerBadgeShown) {
|
const auto storiesUser = historyForCornerBadge
|
||||||
|
? historyForCornerBadge->peer->asUser()
|
||||||
|
: nullptr;
|
||||||
|
const auto storiesShown = (storiesUser
|
||||||
|
&& storiesUser->hasActiveStories()) ? 1 : 0;
|
||||||
|
const auto storiesUnread = (storiesShown
|
||||||
|
&& storiesUser->hasUnreadStories()) ? 1 : 0;
|
||||||
|
if (!historyForCornerBadge || (!cornerBadgeShown && !storiesShown)) {
|
||||||
BasicRow::paintUserpic(
|
BasicRow::paintUserpic(
|
||||||
p,
|
p,
|
||||||
peer,
|
peer,
|
||||||
|
@ -415,12 +467,12 @@ void Row::paintUserpic(
|
||||||
}
|
}
|
||||||
ensureCornerBadgeUserpic();
|
ensureCornerBadgeUserpic();
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
const auto added = std::max({
|
const auto framePadding = std::max({
|
||||||
-st::dialogsCallBadgeSkip.x(),
|
-st::dialogsCallBadgeSkip.x(),
|
||||||
-st::dialogsCallBadgeSkip.y(),
|
-st::dialogsCallBadgeSkip.y(),
|
||||||
0 });
|
st::lineWidth * 2 });
|
||||||
const auto frameSide = (context.st->photoSize + added)
|
const auto frameSide = (2 * framePadding + context.st->photoSize)
|
||||||
* style::DevicePixelRatio();
|
* ratio;
|
||||||
const auto frameSize = QSize(frameSide, frameSide);
|
const auto frameSize = QSize(frameSide, frameSide);
|
||||||
if (_cornerBadgeUserpic->frame.size() != frameSize) {
|
if (_cornerBadgeUserpic->frame.size() != frameSize) {
|
||||||
_cornerBadgeUserpic->frame = QImage(
|
_cornerBadgeUserpic->frame = QImage(
|
||||||
|
@ -432,6 +484,7 @@ void Row::paintUserpic(
|
||||||
key.first += peer->messagesTTL();
|
key.first += peer->messagesTTL();
|
||||||
const auto frameIndex = videoUserpic ? videoUserpic->frameIndex() : -1;
|
const auto frameIndex = videoUserpic ? videoUserpic->frameIndex() : -1;
|
||||||
const auto paletteVersion = style::PaletteVersion();
|
const auto paletteVersion = style::PaletteVersion();
|
||||||
|
const auto active = context.active ? 1 : 0;
|
||||||
const auto keyChanged = (_cornerBadgeUserpic->key != key)
|
const auto keyChanged = (_cornerBadgeUserpic->key != key)
|
||||||
|| (_cornerBadgeUserpic->paletteVersion != paletteVersion);
|
|| (_cornerBadgeUserpic->paletteVersion != paletteVersion);
|
||||||
if (keyChanged) {
|
if (keyChanged) {
|
||||||
|
@ -439,24 +492,29 @@ void Row::paintUserpic(
|
||||||
}
|
}
|
||||||
if (keyChanged
|
if (keyChanged
|
||||||
|| !_cornerBadgeUserpic->layersManager.isFinished()
|
|| !_cornerBadgeUserpic->layersManager.isFinished()
|
||||||
|| _cornerBadgeUserpic->active != context.active
|
|| _cornerBadgeUserpic->active != active
|
||||||
|| _cornerBadgeUserpic->frameIndex != frameIndex
|
|| _cornerBadgeUserpic->frameIndex != frameIndex
|
||||||
|
|| _cornerBadgeUserpic->storiesShown != storiesShown
|
||||||
|
|| _cornerBadgeUserpic->storiesUnread != storiesUnread
|
||||||
|| videoUserpic) {
|
|| videoUserpic) {
|
||||||
_cornerBadgeUserpic->key = key;
|
_cornerBadgeUserpic->key = key;
|
||||||
_cornerBadgeUserpic->paletteVersion = paletteVersion;
|
_cornerBadgeUserpic->paletteVersion = paletteVersion;
|
||||||
_cornerBadgeUserpic->active = context.active;
|
_cornerBadgeUserpic->active = active;
|
||||||
|
_cornerBadgeUserpic->storiesShown = storiesShown;
|
||||||
|
_cornerBadgeUserpic->storiesUnread = storiesUnread;
|
||||||
_cornerBadgeUserpic->frameIndex = frameIndex;
|
_cornerBadgeUserpic->frameIndex = frameIndex;
|
||||||
_cornerBadgeUserpic->layersManager.markFrameShown();
|
_cornerBadgeUserpic->layersManager.markFrameShown();
|
||||||
PaintCornerBadgeFrame(
|
PaintCornerBadgeFrame(
|
||||||
_cornerBadgeUserpic.get(),
|
_cornerBadgeUserpic.get(),
|
||||||
|
framePadding,
|
||||||
peer,
|
peer,
|
||||||
videoUserpic,
|
videoUserpic,
|
||||||
userpicView(),
|
userpicView(),
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
context.st->padding.left(),
|
context.st->padding.left() - framePadding,
|
||||||
context.st->padding.top(),
|
context.st->padding.top() - framePadding,
|
||||||
_cornerBadgeUserpic->frame);
|
_cornerBadgeUserpic->frame);
|
||||||
if (historyForCornerBadge->peer->isUser()) {
|
if (historyForCornerBadge->peer->isUser()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -167,11 +167,13 @@ private:
|
||||||
struct CornerBadgeUserpic {
|
struct CornerBadgeUserpic {
|
||||||
InMemoryKey key;
|
InMemoryKey key;
|
||||||
CornerLayersManager layersManager;
|
CornerLayersManager layersManager;
|
||||||
int paletteVersion = 0;
|
|
||||||
int frameIndex = -1;
|
|
||||||
bool active = false;
|
|
||||||
QImage frame;
|
QImage frame;
|
||||||
QImage cacheTTL;
|
QImage cacheTTL;
|
||||||
|
int frameIndex = -1;
|
||||||
|
int paletteVersion : 24 = 0;
|
||||||
|
int storiesShown : 1 = 0;
|
||||||
|
int storiesUnread : 1 = 0;
|
||||||
|
int active : 1 = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void setCornerBadgeShown(
|
void setCornerBadgeShown(
|
||||||
|
@ -180,6 +182,7 @@ private:
|
||||||
void ensureCornerBadgeUserpic() const;
|
void ensureCornerBadgeUserpic() const;
|
||||||
static void PaintCornerBadgeFrame(
|
static void PaintCornerBadgeFrame(
|
||||||
not_null<CornerBadgeUserpic*> data,
|
not_null<CornerBadgeUserpic*> data,
|
||||||
|
int framePadding,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Ui::VideoUserpic *videoUserpic,
|
Ui::VideoUserpic *videoUserpic,
|
||||||
Ui::PeerUserpicView &view,
|
Ui::PeerUserpicView &view,
|
||||||
|
|
|
@ -503,6 +503,14 @@ void Widget::chosenRow(const ChosenRow &row) {
|
||||||
return;
|
return;
|
||||||
} else if (history) {
|
} else if (history) {
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
|
if (const auto user = history->peer->asUser()) {
|
||||||
|
if (row.message.fullId.msg == ShowAtUnreadMsgId) {
|
||||||
|
if (row.userpicClick && user->hasActiveStories()) {
|
||||||
|
controller()->openPeerStories(user->id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const auto showAtMsgId = controller()->uniqueChatsInSearchResults()
|
const auto showAtMsgId = controller()->uniqueChatsInSearchResults()
|
||||||
? ShowAtUnreadMsgId
|
? ShowAtUnreadMsgId
|
||||||
: row.message.fullId.msg;
|
: row.message.fullId.msg;
|
||||||
|
|
Loading…
Add table
Reference in a new issue