mirror of https://github.com/procxx/kepka.git
Support basic feed display in chats list.
This commit is contained in:
parent
9d2239291d
commit
782e70b171
|
@ -7,17 +7,126 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
|
|
||||||
|
#include "dialogs/dialogs_key.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
Feed::Feed(FeedId id) : _id(id) {
|
FeedPosition::FeedPosition(const MTPFeedPosition &position)
|
||||||
|
: date(position.c_feedPosition().vdate.v)
|
||||||
|
, peerId(peerFromMTP(position.c_feedPosition().vpeer))
|
||||||
|
, msgId(position.c_feedPosition().vid.v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FeedPosition::FeedPosition(not_null<HistoryItem*> item)
|
||||||
|
: date(toServerTime(item->date.toTime_t()).v)
|
||||||
|
, peerId(item->history()->peer->id)
|
||||||
|
, msgId(item->id) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FeedPosition::operator<(const FeedPosition &other) const {
|
||||||
|
if (date < other.date) {
|
||||||
|
return true;
|
||||||
|
} else if (other.date < date) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto peer = peerToBareInt(peerId);
|
||||||
|
const auto otherPeer = peerToBareInt(other.peerId);
|
||||||
|
if (peer < otherPeer) {
|
||||||
|
return true;
|
||||||
|
} else if (otherPeer < peer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (msgId < other.msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Feed::Feed(FeedId id)
|
||||||
|
: Entry(this)
|
||||||
|
, _id(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::registerOne(not_null<ChannelData*> channel) {
|
void Feed::registerOne(not_null<ChannelData*> channel) {
|
||||||
_channels.emplace(channel);
|
const auto history = App::history(channel);
|
||||||
|
if (!base::contains(_channels, history)) {
|
||||||
|
_channels.push_back(history);
|
||||||
|
if (history->lastMsg) {
|
||||||
|
updateLastMessage(history->lastMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::unregisterOne(not_null<ChannelData*> channel) {
|
void Feed::unregisterOne(not_null<ChannelData*> channel) {
|
||||||
_channels.remove(channel);
|
const auto history = App::history(channel);
|
||||||
|
_channels.erase(ranges::remove(_channels, history), end(_channels));
|
||||||
|
if (_lastMessage->history() == history) {
|
||||||
|
messageRemoved(_lastMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Feed::updateLastMessage(not_null<HistoryItem*> item) {
|
||||||
|
if (justSetLastMessage(item)) {
|
||||||
|
setChatsListDate(_lastMessage->date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Feed::loadUserpic() {
|
||||||
|
constexpr auto kPaintUserpicsCount = 4;
|
||||||
|
auto load = kPaintUserpicsCount;
|
||||||
|
for (const auto channel : _channels) {
|
||||||
|
channel->peer->loadUserpic();
|
||||||
|
if (!--load) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Feed::paintUserpic(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int size) const {
|
||||||
|
const auto small = (size - st::lineWidth) / 2;
|
||||||
|
const auto delta = size - small;
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto channel : _channels) {
|
||||||
|
channel->peer->paintUserpic(p, x, y, small);
|
||||||
|
switch (++index) {
|
||||||
|
case 1:
|
||||||
|
case 3: x += delta; break;
|
||||||
|
case 2: x -= delta; y += delta; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
|
||||||
|
if (_lastMessage && FeedPosition(item) <= FeedPosition(_lastMessage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_lastMessage = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Feed::messageRemoved(not_null<HistoryItem*> item) {
|
||||||
|
if (_lastMessage == item) {
|
||||||
|
recountLastMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Feed::historyCleared(not_null<History*> history) {
|
||||||
|
if (_lastMessage->history() == history) {
|
||||||
|
recountLastMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Feed::recountLastMessage() {
|
||||||
|
_lastMessage = nullptr;
|
||||||
|
for (const auto history : _channels) {
|
||||||
|
if (const auto last = history->lastMsg) {
|
||||||
|
justSetLastMessage(last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_lastMessage) {
|
||||||
|
setChatsListDate(_lastMessage->date);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
|
void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
|
||||||
|
@ -25,14 +134,8 @@ void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
|
||||||
_unreadMutedCount = unreadMutedCount;
|
_unreadMutedCount = unreadMutedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::cachePinnedIndex(int index) {
|
void Feed::setUnreadPosition(const FeedPosition &position) {
|
||||||
_pinnedIndex = index;
|
_unreadPosition = position;
|
||||||
}
|
|
||||||
|
|
||||||
uint64 Feed::sortKeyInChatList() const {
|
|
||||||
return 0ULL;/* isPinnedDialog()
|
|
||||||
? pinnedDialogPos(_pinnedIndex)
|
|
||||||
: dialogPosFromDate(chatListDate());*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -7,11 +7,47 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "dialogs/dialogs_entry.h"
|
||||||
|
|
||||||
class ChannelData;
|
class ChannelData;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
class Feed {
|
struct FeedPosition {
|
||||||
|
FeedPosition() = default;
|
||||||
|
FeedPosition(const MTPFeedPosition &position);
|
||||||
|
FeedPosition(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return (msgId != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const FeedPosition &other) const;
|
||||||
|
inline bool operator>(const FeedPosition &other) const {
|
||||||
|
return other < *this;
|
||||||
|
}
|
||||||
|
inline bool operator<=(const FeedPosition &other) const {
|
||||||
|
return !(other < *this);
|
||||||
|
}
|
||||||
|
inline bool operator>=(const FeedPosition &other) const {
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
inline bool operator==(const FeedPosition &other) const {
|
||||||
|
return (date == other.date)
|
||||||
|
&& (peerId == other.peerId)
|
||||||
|
&& (msgId == other.msgId);
|
||||||
|
}
|
||||||
|
inline bool operator!=(const FeedPosition &other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId date = 0;
|
||||||
|
PeerId peerId = 0;
|
||||||
|
MsgId msgId = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Feed : public Dialogs::Entry {
|
||||||
public:
|
public:
|
||||||
Feed(FeedId id);
|
Feed(FeedId id);
|
||||||
|
|
||||||
|
@ -21,20 +57,44 @@ public:
|
||||||
void registerOne(not_null<ChannelData*> channel);
|
void registerOne(not_null<ChannelData*> channel);
|
||||||
void unregisterOne(not_null<ChannelData*> channel);
|
void unregisterOne(not_null<ChannelData*> channel);
|
||||||
|
|
||||||
void setUnreadCounts(int unreadCount, int unreadMutedCount);
|
void updateLastMessage(not_null<HistoryItem*> item);
|
||||||
|
void messageRemoved(not_null<HistoryItem*> item);
|
||||||
|
void historyCleared(not_null<History*> history);
|
||||||
|
|
||||||
bool isPinnedDialog() const {
|
void setUnreadCounts(int unreadCount, int unreadMutedCount);
|
||||||
return _pinnedIndex > 0;
|
void setUnreadPosition(const FeedPosition &position);
|
||||||
|
|
||||||
|
bool toImportant() const override {
|
||||||
|
return false; // TODO feeds workmode
|
||||||
}
|
}
|
||||||
void cachePinnedIndex(int index);
|
int chatListUnreadCount() const override {
|
||||||
uint64 sortKeyInChatList() const;
|
return _unreadCount;
|
||||||
|
}
|
||||||
|
bool chatListMutedBadge() const override {
|
||||||
|
return _unreadCount <= _unreadMutedCount;
|
||||||
|
}
|
||||||
|
HistoryItem *chatsListItem() const override {
|
||||||
|
return _lastMessage;
|
||||||
|
}
|
||||||
|
void loadUserpic() override;
|
||||||
|
void paintUserpic(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int size) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void recountLastMessage();
|
||||||
|
bool justSetLastMessage(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
FeedId _id = 0;
|
FeedId _id = 0;
|
||||||
base::flat_set<not_null<ChannelData*>> _channels;
|
std::vector<not_null<History*>> _channels;
|
||||||
|
|
||||||
|
HistoryItem *_lastMessage = nullptr;
|
||||||
|
|
||||||
|
FeedPosition _unreadPosition;
|
||||||
int _unreadCount = 0;
|
int _unreadCount = 0;
|
||||||
int _unreadMutedCount = 0;
|
int _unreadMutedCount = 0;
|
||||||
int _pinnedIndex = 0;
|
|
||||||
bool _complete = false;
|
bool _complete = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -176,8 +176,7 @@ TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now) {
|
||||||
}
|
}
|
||||||
const auto online = user->onlineTill;
|
const auto online = user->onlineTill;
|
||||||
const auto fromDate = [](const QDate &date) {
|
const auto fromDate = [](const QDate &date) {
|
||||||
const auto shift = (unixtime() - myunixtime());
|
return toServerTime(QDateTime(date).toTime_t()).v;
|
||||||
return static_cast<TimeId>(QDateTime(date).toTime_t()) + shift;
|
|
||||||
};
|
};
|
||||||
if (online <= 0) {
|
if (online <= 0) {
|
||||||
switch (online) {
|
switch (online) {
|
||||||
|
|
|
@ -235,8 +235,8 @@ void Session::reorderTwoPinnedDialogs(
|
||||||
Assert(index2 >= 0 && index2 < order.size());
|
Assert(index2 >= 0 && index2 < order.size());
|
||||||
Assert(index1 != index2);
|
Assert(index1 != index2);
|
||||||
std::swap(_pinnedDialogs[index1], _pinnedDialogs[index2]);
|
std::swap(_pinnedDialogs[index1], _pinnedDialogs[index2]);
|
||||||
key1.cachePinnedIndex(index2 + 1);
|
key1.entry()->cachePinnedIndex(index2 + 1);
|
||||||
key2.cachePinnedIndex(index1 + 1);
|
key2.entry()->cachePinnedIndex(index1 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
|
void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
|
||||||
|
@ -249,26 +249,31 @@ void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
|
||||||
Assert(alreadyIndex < count);
|
Assert(alreadyIndex < count);
|
||||||
for (auto index = alreadyIndex + 1; index != count; ++index) {
|
for (auto index = alreadyIndex + 1; index != count; ++index) {
|
||||||
_pinnedDialogs[index - 1] = std::move(_pinnedDialogs[index]);
|
_pinnedDialogs[index - 1] = std::move(_pinnedDialogs[index]);
|
||||||
_pinnedDialogs[index - 1].cachePinnedIndex(index);
|
_pinnedDialogs[index - 1].entry()->cachePinnedIndex(index);
|
||||||
}
|
}
|
||||||
_pinnedDialogs.back() = std::move(saved);
|
_pinnedDialogs.back() = std::move(saved);
|
||||||
_pinnedDialogs.back().cachePinnedIndex(count);
|
_pinnedDialogs.back().entry()->cachePinnedIndex(count);
|
||||||
} else {
|
} else {
|
||||||
_pinnedDialogs.push_back(key);
|
_pinnedDialogs.push_back(key);
|
||||||
if (_pinnedDialogs.size() > Global::PinnedDialogsCountMax()) {
|
if (_pinnedDialogs.size() > Global::PinnedDialogsCountMax()) {
|
||||||
_pinnedDialogs.front().cachePinnedIndex(0);
|
_pinnedDialogs.front().entry()->cachePinnedIndex(0);
|
||||||
_pinnedDialogs.pop_front();
|
_pinnedDialogs.pop_front();
|
||||||
|
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
for (const auto &pinned : _pinnedDialogs) {
|
for (const auto &pinned : _pinnedDialogs) {
|
||||||
pinned.cachePinnedIndex(++index);
|
pinned.entry()->cachePinnedIndex(++index);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
key.cachePinnedIndex(_pinnedDialogs.size());
|
key.entry()->cachePinnedIndex(_pinnedDialogs.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!pinned && already != _pinnedDialogs.end()) {
|
} else if (!pinned && already != _pinnedDialogs.end()) {
|
||||||
|
key.entry()->cachePinnedIndex(0);
|
||||||
_pinnedDialogs.erase(already);
|
_pinnedDialogs.erase(already);
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto &pinned : _pinnedDialogs) {
|
||||||
|
pinned.entry()->cachePinnedIndex(++index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
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 "base/flat_map.h"
|
|
||||||
|
|
||||||
namespace Dialogs {
|
|
||||||
|
|
||||||
class Row;
|
|
||||||
using RowsByLetter = base::flat_map<QChar, Row*>;
|
|
||||||
|
|
||||||
enum class SortMode {
|
|
||||||
Date = 0x00,
|
|
||||||
Name = 0x01,
|
|
||||||
Add = 0x02,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Mode {
|
|
||||||
All = 0x00,
|
|
||||||
Important = 0x01,
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Dialogs
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
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/dialogs_entry.h"
|
||||||
|
|
||||||
|
#include "dialogs/dialogs_key.h"
|
||||||
|
#include "dialogs/dialogs_indexed_list.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
|
namespace Dialogs {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
auto DialogsPosToTopShift = 0;
|
||||||
|
|
||||||
|
uint64 DialogPosFromDate(const QDateTime &date) {
|
||||||
|
if (date.isNull()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (uint64(date.toTime_t()) << 32) | (++DialogsPosToTopShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 PinnedDialogPos(int pinnedIndex) {
|
||||||
|
return 0xFFFFFFFF00000000ULL + pinnedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool MessageIsLess(not_null<HistoryItem*> a, not_null<HistoryItem*> b) {
|
||||||
|
if (a->date < b->date) {
|
||||||
|
return true;
|
||||||
|
} else if (b->date < a->date) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto apeer = a->history()->peer->bareId();
|
||||||
|
const auto bpeer = b->history()->peer->bareId();
|
||||||
|
if (apeer < bpeer) {
|
||||||
|
return true;
|
||||||
|
} else if (bpeer < apeer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return a->id < b->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry::Entry(const Key &key)
|
||||||
|
: lastItemTextCache(st::dialogsTextWidthMin)
|
||||||
|
, _key(key) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::cachePinnedIndex(int index) {
|
||||||
|
if (_pinnedIndex != index) {
|
||||||
|
const auto wasPinned = isPinnedDialog();
|
||||||
|
_pinnedIndex = index;
|
||||||
|
updateChatListSortPosition();
|
||||||
|
updateChatListEntry();
|
||||||
|
if (wasPinned != isPinnedDialog()) {
|
||||||
|
changedChatListPinHook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::updateChatListSortPosition() {
|
||||||
|
_sortKeyInChatList = isPinnedDialog()
|
||||||
|
? PinnedDialogPos(_pinnedIndex)
|
||||||
|
: DialogPosFromDate(adjustChatListDate());
|
||||||
|
if (auto m = App::main()) {
|
||||||
|
if (needUpdateInChatList()) {
|
||||||
|
if (_sortKeyInChatList) {
|
||||||
|
m->createDialog(_key);
|
||||||
|
updateChatListEntry();
|
||||||
|
} else {
|
||||||
|
removeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::removeDialog() {
|
||||||
|
if (const auto main = App::main()) {
|
||||||
|
main->removeDialog(_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionChange Entry::adjustByPosInChatList(
|
||||||
|
Mode list,
|
||||||
|
not_null<IndexedList*> indexed) {
|
||||||
|
const auto lnk = mainChatListLink(list);
|
||||||
|
const auto movedFrom = lnk->pos();
|
||||||
|
indexed->adjustByPos(chatListLinks(list));
|
||||||
|
const auto movedTo = lnk->pos();
|
||||||
|
return { movedFrom, movedTo };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::setChatsListDate(const QDateTime &date) {
|
||||||
|
if (!_lastMessageDate.isNull() && _lastMessageDate >= date) {
|
||||||
|
if (!needUpdateInChatList() || !inChatList(Dialogs::Mode::All)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_lastMessageDate = date;
|
||||||
|
updateChatListSortPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Entry::posInChatList(Dialogs::Mode list) const {
|
||||||
|
return mainChatListLink(list)->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<Row*> Entry::addToChatList(
|
||||||
|
Mode list,
|
||||||
|
not_null<IndexedList*> indexed) {
|
||||||
|
if (!inChatList(list)) {
|
||||||
|
chatListLinks(list) = indexed->addToEnd(_key);
|
||||||
|
changedInChatListHook(list, true);
|
||||||
|
}
|
||||||
|
return mainChatListLink(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::removeFromChatList(
|
||||||
|
Dialogs::Mode list,
|
||||||
|
not_null<Dialogs::IndexedList*> indexed) {
|
||||||
|
if (inChatList(list)) {
|
||||||
|
indexed->del(_key);
|
||||||
|
chatListLinks(list).clear();
|
||||||
|
changedInChatListHook(list, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::removeChatListEntryByLetter(Mode list, QChar letter) {
|
||||||
|
Expects(letter != 0);
|
||||||
|
|
||||||
|
if (inChatList(list)) {
|
||||||
|
chatListLinks(list).remove(letter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::addChatListEntryByLetter(
|
||||||
|
Mode list,
|
||||||
|
QChar letter,
|
||||||
|
not_null<Row*> row) {
|
||||||
|
Expects(letter != 0);
|
||||||
|
|
||||||
|
if (inChatList(list)) {
|
||||||
|
chatListLinks(list).emplace(letter, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::updateChatListEntry() const {
|
||||||
|
if (const auto main = App::main()) {
|
||||||
|
if (inChatList(Mode::All)) {
|
||||||
|
main->dlgUpdated(
|
||||||
|
Mode::All,
|
||||||
|
mainChatListLink(Mode::All));
|
||||||
|
if (inChatList(Mode::Important)) {
|
||||||
|
main->dlgUpdated(
|
||||||
|
Mode::Important,
|
||||||
|
mainChatListLink(Mode::Important));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dialogs
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
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 "base/flat_map.h"
|
||||||
|
|
||||||
|
#include "dialogs/dialogs_key.h"
|
||||||
|
|
||||||
|
namespace Dialogs {
|
||||||
|
|
||||||
|
class Row;
|
||||||
|
class IndexedList;
|
||||||
|
using RowsByLetter = base::flat_map<QChar, not_null<Row*>>;
|
||||||
|
|
||||||
|
enum class SortMode {
|
||||||
|
Date = 0x00,
|
||||||
|
Name = 0x01,
|
||||||
|
Add = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
All = 0x00,
|
||||||
|
Important = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PositionChange {
|
||||||
|
int movedFrom;
|
||||||
|
int movedTo;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool MessageIsLess(not_null<HistoryItem*> a, not_null<HistoryItem*> b);
|
||||||
|
|
||||||
|
class Entry {
|
||||||
|
public:
|
||||||
|
Entry(const Key &key);
|
||||||
|
|
||||||
|
PositionChange adjustByPosInChatList(
|
||||||
|
Mode list,
|
||||||
|
not_null<IndexedList*> indexed);
|
||||||
|
bool inChatList(Mode list) const {
|
||||||
|
return !chatListLinks(list).empty();
|
||||||
|
}
|
||||||
|
int posInChatList(Mode list) const;
|
||||||
|
not_null<Row*> addToChatList(Mode list, not_null<IndexedList*> indexed);
|
||||||
|
void removeFromChatList(Mode list, not_null<IndexedList*> indexed);
|
||||||
|
void removeChatListEntryByLetter(Mode list, QChar letter);
|
||||||
|
void addChatListEntryByLetter(
|
||||||
|
Mode list,
|
||||||
|
QChar letter,
|
||||||
|
not_null<Row*> row);
|
||||||
|
void updateChatListEntry() const;
|
||||||
|
bool isPinnedDialog() const {
|
||||||
|
return _pinnedIndex > 0;
|
||||||
|
}
|
||||||
|
void cachePinnedIndex(int index);
|
||||||
|
uint64 sortKeyInChatList() const {
|
||||||
|
return _sortKeyInChatList;
|
||||||
|
}
|
||||||
|
void updateChatListSortPosition();
|
||||||
|
void setChatsListDate(const QDateTime &date);
|
||||||
|
|
||||||
|
virtual bool toImportant() const = 0;
|
||||||
|
|
||||||
|
virtual bool needUpdateInChatList() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int chatListUnreadCount() const = 0;
|
||||||
|
virtual bool chatListMutedBadge() const = 0;
|
||||||
|
virtual HistoryItem *chatsListItem() const = 0;
|
||||||
|
|
||||||
|
virtual void loadUserpic() = 0;
|
||||||
|
virtual void paintUserpic(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int size) const = 0;
|
||||||
|
void paintUserpicLeft(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int w,
|
||||||
|
int size) const {
|
||||||
|
paintUserpic(p, rtl() ? (w - x - size) : x, y, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime chatsListDate() const {
|
||||||
|
return _lastMessageDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Entry() = default;
|
||||||
|
|
||||||
|
mutable const HistoryItem *textCachedFor = nullptr; // cache
|
||||||
|
mutable Text lastItemTextCache;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual QDateTime adjustChatListDate() const {
|
||||||
|
return chatsListDate();
|
||||||
|
}
|
||||||
|
virtual void removeDialog();
|
||||||
|
virtual void changedInChatListHook(Dialogs::Mode list, bool added) {
|
||||||
|
}
|
||||||
|
virtual void changedChatListPinHook() {
|
||||||
|
}
|
||||||
|
|
||||||
|
RowsByLetter &chatListLinks(Mode list) {
|
||||||
|
return _chatListLinks[static_cast<int>(list)];
|
||||||
|
}
|
||||||
|
const RowsByLetter &chatListLinks(Mode list) const {
|
||||||
|
return _chatListLinks[static_cast<int>(list)];
|
||||||
|
}
|
||||||
|
Row *mainChatListLink(Mode list) const {
|
||||||
|
auto it = chatListLinks(list).find(0);
|
||||||
|
Assert(it != chatListLinks(list).cend());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialogs::Key _key;
|
||||||
|
RowsByLetter _chatListLinks[2];
|
||||||
|
uint64 _sortKeyInChatList = 0;
|
||||||
|
int _pinnedIndex = 0;
|
||||||
|
QDateTime _lastMessageDate;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Dialogs
|
|
@ -152,14 +152,12 @@ void IndexedList::adjustByName(
|
||||||
|
|
||||||
void IndexedList::adjustNames(
|
void IndexedList::adjustNames(
|
||||||
Mode list,
|
Mode list,
|
||||||
Key key,
|
not_null<History*> history,
|
||||||
const PeerData::NameFirstChars &oldChars) {
|
const PeerData::NameFirstChars &oldChars) {
|
||||||
|
const auto key = Dialogs::Key(history);
|
||||||
auto mainRow = _list.getRow(key);
|
auto mainRow = _list.getRow(key);
|
||||||
if (!mainRow) return;
|
if (!mainRow) return;
|
||||||
|
|
||||||
// #TODO feeds dialogs
|
|
||||||
auto history = mainRow->history();
|
|
||||||
|
|
||||||
PeerData::NameFirstChars toRemove = oldChars, toAdd;
|
PeerData::NameFirstChars toRemove = oldChars, toAdd;
|
||||||
for (auto ch : key.nameFirstChars()) {
|
for (auto ch : key.nameFirstChars()) {
|
||||||
auto j = toRemove.find(ch);
|
auto j = toRemove.find(ch);
|
||||||
|
|
|
@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "dialogs/dialogs_common.h"
|
#include "dialogs/dialogs_entry.h"
|
||||||
#include "dialogs/dialogs_list.h"
|
#include "dialogs/dialogs_list.h"
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
|
@ -80,7 +80,7 @@ private:
|
||||||
const PeerData::NameFirstChars &oldChars);
|
const PeerData::NameFirstChars &oldChars);
|
||||||
void adjustNames(
|
void adjustNames(
|
||||||
Mode list,
|
Mode list,
|
||||||
Key key,
|
not_null<History*> history,
|
||||||
const PeerData::NameFirstChars &oldChars);
|
const PeerData::NameFirstChars &oldChars);
|
||||||
|
|
||||||
SortMode _sortMode;
|
SortMode _sortMode;
|
||||||
|
|
|
@ -186,17 +186,24 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
|
||||||
p.translate(0, st::dialogsImportantBarHeight);
|
p.translate(0, st::dialogsImportantBarHeight);
|
||||||
}
|
}
|
||||||
auto otherStart = rows->size() * st::dialogsRowHeight;
|
auto otherStart = rows->size() * st::dialogsRowHeight;
|
||||||
auto active = App::main()->activePeer();
|
// #TODO feeds show
|
||||||
// #TODO feeds dialogs
|
const auto active = [&] {
|
||||||
auto selected = _menuPeer
|
if (const auto peer = App::main()->activePeer()) {
|
||||||
? _menuPeer
|
if (const auto history = App::historyLoaded(peer)) {
|
||||||
|
return Dialogs::Key(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Dialogs::Key();
|
||||||
|
}();
|
||||||
|
const auto selected = _menuKey
|
||||||
|
? _menuKey
|
||||||
: (isPressed()
|
: (isPressed()
|
||||||
? (_pressed
|
? (_pressed
|
||||||
? _pressed->history()->peer
|
? _pressed->key()
|
||||||
: nullptr)
|
: Dialogs::Key())
|
||||||
: (_selected
|
: (_selected
|
||||||
? _selected->history()->peer
|
? _selected->key()
|
||||||
: nullptr));
|
: Dialogs::Key()));
|
||||||
if (otherStart) {
|
if (otherStart) {
|
||||||
auto reorderingPinned = (_aboveIndex >= 0 && !_pinnedRows.empty());
|
auto reorderingPinned = (_aboveIndex >= 0 && !_pinnedRows.empty());
|
||||||
auto &list = rows->all();
|
auto &list = rows->all();
|
||||||
|
@ -302,17 +309,31 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
|
||||||
auto to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _filterResults.size());
|
auto to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _filterResults.size());
|
||||||
p.translate(0, from * st::dialogsRowHeight);
|
p.translate(0, from * st::dialogsRowHeight);
|
||||||
if (from < _filterResults.size()) {
|
if (from < _filterResults.size()) {
|
||||||
auto activePeer = App::main()->activePeer();
|
const auto activePeer = App::main()->activePeer();
|
||||||
auto activeMsgId = App::main()->activeMsgId();
|
const auto activeMsgId = App::main()->activeMsgId();
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
auto row = _filterResults[from];
|
const auto row = _filterResults[from];
|
||||||
// #TODO feeds dialogs
|
const auto key = row->key();
|
||||||
auto peer = row->history()->peer;
|
const auto history = key.history();
|
||||||
auto active = !activeMsgId && ((peer == activePeer)
|
const auto peer = history ? history->peer : nullptr;
|
||||||
|| (peer->migrateTo()
|
const auto active = !activeMsgId
|
||||||
&& peer->migrateTo() == activePeer));
|
&& peer
|
||||||
auto selected = _menuPeer ? (peer == _menuPeer) : (from == (isPressed() ? _filteredPressed : _filteredSelected));
|
&& activePeer
|
||||||
Dialogs::Layout::RowPainter::paint(p, _filterResults[from], fullWidth, active, selected, paintingOther, ms);
|
&& ((peer == activePeer)
|
||||||
|
|| (peer->migrateTo() == activePeer));
|
||||||
|
const auto selected = _menuKey
|
||||||
|
? (key == _menuKey)
|
||||||
|
: (from == (isPressed()
|
||||||
|
? _filteredPressed
|
||||||
|
: _filteredSelected));
|
||||||
|
Dialogs::Layout::RowPainter::paint(
|
||||||
|
p,
|
||||||
|
_filterResults[from],
|
||||||
|
fullWidth,
|
||||||
|
active,
|
||||||
|
selected,
|
||||||
|
paintingOther,
|
||||||
|
ms);
|
||||||
p.translate(0, st::dialogsRowHeight);
|
p.translate(0, st::dialogsRowHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,10 +356,15 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
|
||||||
auto activePeer = App::main()->activePeer();
|
auto activePeer = App::main()->activePeer();
|
||||||
auto activeMsgId = App::main()->activeMsgId();
|
auto activeMsgId = App::main()->activeMsgId();
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
auto &result = _peerSearchResults[from];
|
const auto &result = _peerSearchResults[from];
|
||||||
auto peer = result->peer;
|
const auto peer = result->peer;
|
||||||
auto active = ((peer == activePeer) || (peer->migrateTo() && peer->migrateTo() == activePeer)) && !activeMsgId;
|
const auto active = !activeMsgId
|
||||||
auto selected = false ? (peer == _menuPeer) : (from == (isPressed() ? _peerSearchPressed : _peerSearchSelected));
|
&& activePeer
|
||||||
|
&& ((peer == activePeer)
|
||||||
|
|| (peer->migrateTo() == activePeer));
|
||||||
|
const auto selected = (from == (isPressed()
|
||||||
|
? _peerSearchPressed
|
||||||
|
: _peerSearchSelected));
|
||||||
paintPeerSearchResult(p, result.get(), fullWidth, active, selected, paintingOther, ms);
|
paintPeerSearchResult(p, result.get(), fullWidth, active, selected, paintingOther, ms);
|
||||||
p.translate(0, st::dialogsRowHeight);
|
p.translate(0, st::dialogsRowHeight);
|
||||||
}
|
}
|
||||||
|
@ -377,11 +403,17 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
|
||||||
auto activePeer = App::main()->activePeer();
|
auto activePeer = App::main()->activePeer();
|
||||||
auto activeMsgId = App::main()->activeMsgId();
|
auto activeMsgId = App::main()->activeMsgId();
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
auto &result = _searchResults[from];
|
const auto &result = _searchResults[from];
|
||||||
auto item = result->item();
|
const auto item = result->item();
|
||||||
auto peer = item->history()->peer;
|
const auto peer = item->history()->peer;
|
||||||
auto active = (peer == activePeer && item->id == activeMsgId) || (peer->migrateTo() && peer->migrateTo() == activePeer && item->id == -activeMsgId);
|
const auto active = (peer == activePeer
|
||||||
auto selected = false ? (peer == _menuPeer) : (from == (isPressed() ? _searchedPressed : _searchedSelected));
|
&& item->id == activeMsgId)
|
||||||
|
|| (peer->migrateTo()
|
||||||
|
&& peer->migrateTo() == activePeer
|
||||||
|
&& item->id == -activeMsgId);
|
||||||
|
const auto selected = (from == (isPressed()
|
||||||
|
? _searchedPressed
|
||||||
|
: _searchedSelected));
|
||||||
Dialogs::Layout::RowPainter::paint(p, result.get(), fullWidth, active, selected, paintingOther, ms);
|
Dialogs::Layout::RowPainter::paint(p, result.get(), fullWidth, active, selected, paintingOther, ms);
|
||||||
p.translate(0, st::dialogsRowHeight);
|
p.translate(0, st::dialogsRowHeight);
|
||||||
}
|
}
|
||||||
|
@ -394,8 +426,8 @@ void DialogsInner::paintDialog(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
not_null<Dialogs::Row*> row,
|
not_null<Dialogs::Row*> row,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
PeerData *active,
|
Dialogs::Key active,
|
||||||
PeerData *selected,
|
Dialogs::Key selected,
|
||||||
bool onlyBackground,
|
bool onlyBackground,
|
||||||
TimeMs ms) {
|
TimeMs ms) {
|
||||||
auto pos = row->pos();
|
auto pos = row->pos();
|
||||||
|
@ -404,11 +436,8 @@ void DialogsInner::paintDialog(
|
||||||
yadd = qRound(_pinnedRows[pos].yadd.current());
|
yadd = qRound(_pinnedRows[pos].yadd.current());
|
||||||
}
|
}
|
||||||
if (xadd || yadd) p.translate(xadd, yadd);
|
if (xadd || yadd) p.translate(xadd, yadd);
|
||||||
// #TODO feeds dialogs
|
const auto isActive = (row->key() == active);
|
||||||
auto isActive = (row->history()->peer == active)
|
const auto isSelected = (row->key() == selected);
|
||||||
|| (row->history()->peer->migrateTo()
|
|
||||||
&& row->history()->peer->migrateTo() == active);
|
|
||||||
auto isSelected = (row->history()->peer == selected);
|
|
||||||
Dialogs::Layout::RowPainter::paint(
|
Dialogs::Layout::RowPainter::paint(
|
||||||
p,
|
p,
|
||||||
row,
|
row,
|
||||||
|
@ -697,8 +726,7 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) {
|
||||||
auto row = _pressed;
|
auto row = _pressed;
|
||||||
row->addRipple(e->pos() - QPoint(0, dialogsOffset() + _pressed->pos() * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [this, row] {
|
row->addRipple(e->pos() - QPoint(0, dialogsOffset() + _pressed->pos() * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [this, row] {
|
||||||
if (!_a_pinnedShifting.animating()) {
|
if (!_a_pinnedShifting.animating()) {
|
||||||
// #TODO feeds dialogs
|
row->entry()->updateChatListEntry();
|
||||||
row->history()->updateChatListEntry();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_dragStart = e->pos();
|
_dragStart = e->pos();
|
||||||
|
@ -1072,41 +1100,57 @@ void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsInner::createDialog(not_null<History*> history) {
|
void DialogsInner::createDialog(Dialogs::Key key) {
|
||||||
if (history->peer->loadedStatus != PeerData::LoadedStatus::FullLoaded) {
|
if (const auto history = key.history()) {
|
||||||
LOG(("API Error: DialogsInner::createDialog() called for a non loaded peer!"));
|
if (history->peer->loadedStatus
|
||||||
return;
|
!= PeerData::LoadedStatus::FullLoaded) {
|
||||||
|
LOG(("API Error: "
|
||||||
|
"DialogsInner::createDialog() called for a non loaded peer!"
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool creating = !history->inChatList(Dialogs::Mode::All);
|
const auto entry = key.entry();
|
||||||
|
auto creating = !entry->inChatList(Dialogs::Mode::All);
|
||||||
if (creating) {
|
if (creating) {
|
||||||
auto mainRow = history->addToChatList(Dialogs::Mode::All, _dialogs.get());
|
const auto mainRow = entry->addToChatList(
|
||||||
_contactsNoDialogs->del(history, mainRow);
|
Dialogs::Mode::All,
|
||||||
|
_dialogs.get());
|
||||||
|
_contactsNoDialogs->del(key, mainRow);
|
||||||
}
|
}
|
||||||
if (_dialogsImportant && !history->inChatList(Dialogs::Mode::Important) && !history->mute()) {
|
if (_dialogsImportant
|
||||||
|
&& !entry->inChatList(Dialogs::Mode::Important)
|
||||||
|
&& entry->toImportant()) {
|
||||||
if (Global::DialogsMode() == Dialogs::Mode::Important) {
|
if (Global::DialogsMode() == Dialogs::Mode::Important) {
|
||||||
creating = true;
|
creating = true;
|
||||||
}
|
}
|
||||||
history->addToChatList(Dialogs::Mode::Important, _dialogsImportant.get());
|
entry->addToChatList(
|
||||||
|
Dialogs::Mode::Important,
|
||||||
|
_dialogsImportant.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, _dialogs.get());
|
auto changed = entry->adjustByPosInChatList(
|
||||||
|
Dialogs::Mode::All,
|
||||||
|
_dialogs.get());
|
||||||
|
|
||||||
if (_dialogsImportant) {
|
if (_dialogsImportant) {
|
||||||
if (history->mute()) {
|
if (!entry->toImportant()) {
|
||||||
if (Global::DialogsMode() == Dialogs::Mode::Important) {
|
if (Global::DialogsMode() == Dialogs::Mode::Important) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto importantChanged = history->adjustByPosInChatList(Dialogs::Mode::Important, _dialogsImportant.get());
|
const auto importantChanged = entry->adjustByPosInChatList(
|
||||||
|
Dialogs::Mode::Important,
|
||||||
|
_dialogsImportant.get());
|
||||||
if (Global::DialogsMode() == Dialogs::Mode::Important) {
|
if (Global::DialogsMode() == Dialogs::Mode::Important) {
|
||||||
changed = importantChanged;
|
changed = importantChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight;
|
const auto from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight;
|
||||||
int to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight;
|
const auto to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight;
|
||||||
if (!_dragging) {
|
if (!_dragging) {
|
||||||
// Don't jump in chats list scroll position while dragging.
|
// Don't jump in chats list scroll position while dragging.
|
||||||
emit dialogMoved(from, to);
|
emit dialogMoved(from, to);
|
||||||
|
@ -1119,30 +1163,35 @@ void DialogsInner::createDialog(not_null<History*> history) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsInner::removeDialog(not_null<History*> history) {
|
void DialogsInner::removeDialog(Dialogs::Key key) {
|
||||||
// #TODO feeds dialogs
|
if (key == _menuKey && _menu) {
|
||||||
if (history->peer == _menuPeer && _menu) {
|
|
||||||
InvokeQueued(this, [this] { _menu = nullptr; });
|
InvokeQueued(this, [this] { _menu = nullptr; });
|
||||||
}
|
}
|
||||||
if (_selected && _selected->history() == history) {
|
if (_selected && _selected->key() == key) {
|
||||||
_selected = nullptr;
|
_selected = nullptr;
|
||||||
}
|
}
|
||||||
if (_pressed && _pressed->history() == history) {
|
if (_pressed && _pressed->key() == key) {
|
||||||
setPressed(nullptr);
|
setPressed(nullptr);
|
||||||
}
|
}
|
||||||
history->removeFromChatList(Dialogs::Mode::All, _dialogs.get());
|
const auto entry = key.entry();
|
||||||
|
entry->removeFromChatList(
|
||||||
|
Dialogs::Mode::All,
|
||||||
|
_dialogs.get());
|
||||||
if (_dialogsImportant) {
|
if (_dialogsImportant) {
|
||||||
history->removeFromChatList(Dialogs::Mode::Important, _dialogsImportant.get());
|
entry->removeFromChatList(
|
||||||
|
Dialogs::Mode::Important,
|
||||||
|
_dialogsImportant.get());
|
||||||
}
|
}
|
||||||
Auth().notifications().clearFromHistory(history);
|
if (const auto history = key.history()) {
|
||||||
if (_contacts->contains(history)) {
|
Auth().notifications().clearFromHistory(history);
|
||||||
if (!_contactsNoDialogs->contains(history)) {
|
Local::removeSavedPeer(history->peer);
|
||||||
_contactsNoDialogs->addByName(history);
|
}
|
||||||
|
if (_contacts->contains(key)) {
|
||||||
|
if (!_contactsNoDialogs->contains(key)) {
|
||||||
|
_contactsNoDialogs->addByName(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Local::removeSavedPeer(history->peer);
|
|
||||||
|
|
||||||
emit App::main()->dialogsUpdated();
|
emit App::main()->dialogsUpdated();
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
@ -1257,29 +1306,28 @@ void DialogsInner::enterEventHook(QEvent *e) {
|
||||||
updateSelected();
|
updateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsInner::updateSelectedRow(PeerData *peer) {
|
void DialogsInner::updateSelectedRow(Dialogs::Key key) {
|
||||||
// #TODO feeds dialogs
|
|
||||||
if (_state == DefaultState) {
|
if (_state == DefaultState) {
|
||||||
if (peer) {
|
if (key) {
|
||||||
if (auto h = App::historyLoaded(peer->id)) {
|
const auto entry = key.entry();
|
||||||
if (h->inChatList(Global::DialogsMode())) {
|
if (!entry->inChatList(Global::DialogsMode())) {
|
||||||
auto position = h->posInChatList(Global::DialogsMode());
|
return;
|
||||||
auto top = dialogsOffset();
|
|
||||||
if (position >= 0 && position < _pinnedRows.size()) {
|
|
||||||
top += qRound(_pinnedRows[position].yadd.current());
|
|
||||||
}
|
|
||||||
update(0, top + position * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
auto position = entry->posInChatList(Global::DialogsMode());
|
||||||
|
auto top = dialogsOffset();
|
||||||
|
if (position >= 0 && position < _pinnedRows.size()) {
|
||||||
|
top += qRound(_pinnedRows[position].yadd.current());
|
||||||
|
}
|
||||||
|
update(0, top + position * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
|
||||||
} else if (_selected) {
|
} else if (_selected) {
|
||||||
update(0, dialogsOffset() + _selected->pos() * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
|
update(0, dialogsOffset() + _selected->pos() * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
|
||||||
} else if (_importantSwitchSelected) {
|
} else if (_importantSwitchSelected) {
|
||||||
update(0, 0, getFullWidth(), st::dialogsImportantBarHeight);
|
update(0, 0, getFullWidth(), st::dialogsImportantBarHeight);
|
||||||
}
|
}
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
if (peer) {
|
if (key) {
|
||||||
for (auto i = 0, l = _filterResults.size(); i != l; ++i) {
|
for (auto i = 0, l = _filterResults.size(); i != l; ++i) {
|
||||||
if (_filterResults[i]->history()->peer == peer) {
|
if (_filterResults[i]->key() == key) {
|
||||||
update(0, filteredOffset() + i * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
|
update(0, filteredOffset() + i * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1325,37 +1373,46 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
updateSelected();
|
updateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
// #TODO feeds context menu
|
const auto key = [&]() -> Dialogs::Key {
|
||||||
auto history = [&]() -> History* {
|
|
||||||
if (_state == DefaultState) {
|
if (_state == DefaultState) {
|
||||||
if (_selected) {
|
if (_selected) {
|
||||||
return _selected->history();
|
return _selected->key();
|
||||||
}
|
}
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) {
|
if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) {
|
||||||
return _filterResults[_filteredSelected]->history();
|
return _filterResults[_filteredSelected]->key();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return Dialogs::Key();
|
||||||
}();
|
}();
|
||||||
if (!history) return;
|
if (!key) return;
|
||||||
|
|
||||||
_menuPeer = history->peer;
|
_menuKey = key;
|
||||||
if (_pressButton != Qt::LeftButton) {
|
if (_pressButton != Qt::LeftButton) {
|
||||||
mousePressReleased(_pressButton);
|
mousePressReleased(_pressButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
|
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
|
||||||
Window::FillPeerMenu(
|
if (const auto history = key.history()) {
|
||||||
_controller,
|
Window::FillPeerMenu(
|
||||||
_menuPeer,
|
_controller,
|
||||||
[this](const QString &text, base::lambda<void()> callback) {
|
history->peer,
|
||||||
return _menu->addAction(text, std::move(callback));
|
[&](const QString &text, base::lambda<void()> callback) {
|
||||||
},
|
return _menu->addAction(text, std::move(callback));
|
||||||
Window::PeerMenuSource::ChatsList);
|
},
|
||||||
|
Window::PeerMenuSource::ChatsList);
|
||||||
|
} else if (const auto feed = key.feed()) {
|
||||||
|
Window::FillFeedMenu(
|
||||||
|
_controller,
|
||||||
|
feed,
|
||||||
|
[&](const QString &text, base::lambda<void()> callback) {
|
||||||
|
return _menu->addAction(text, std::move(callback));
|
||||||
|
},
|
||||||
|
Window::PeerMenuSource::ChatsList);
|
||||||
|
}
|
||||||
connect(_menu.get(), &QObject::destroyed, [this] {
|
connect(_menu.get(), &QObject::destroyed, [this] {
|
||||||
if (_menuPeer) {
|
if (_menuKey) {
|
||||||
updateSelectedRow(base::take(_menuPeer));
|
updateSelectedRow(base::take(_menuKey));
|
||||||
}
|
}
|
||||||
auto localPos = mapFromGlobal(QCursor::pos());
|
auto localPos = mapFromGlobal(QCursor::pos());
|
||||||
if (rect().contains(localPos)) {
|
if (rect().contains(localPos)) {
|
||||||
|
@ -1428,10 +1485,12 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0));
|
_filterResults.reserve((toFilter ? toFilter->size() : 0)
|
||||||
|
+ (toFilterContacts ? toFilterContacts->size() : 0));
|
||||||
if (toFilter) {
|
if (toFilter) {
|
||||||
for_const (auto row, *toFilter) {
|
for_const (auto row, *toFilter) {
|
||||||
// #TODO feeds dialogs
|
if (!row->history()) continue;
|
||||||
|
// #TODO feeds name
|
||||||
const auto &nameWords = row->history()->peer->nameWords();
|
const auto &nameWords = row->history()->peer->nameWords();
|
||||||
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
|
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
|
||||||
for (fi = fb; fi != fe; ++fi) {
|
for (fi = fb; fi != fe; ++fi) {
|
||||||
|
@ -1452,7 +1511,8 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
|
||||||
}
|
}
|
||||||
if (toFilterContacts) {
|
if (toFilterContacts) {
|
||||||
for_const (auto row, *toFilterContacts) {
|
for_const (auto row, *toFilterContacts) {
|
||||||
// #TODO feeds dialogs
|
if (!row->history()) continue;
|
||||||
|
// #TODO feeds name
|
||||||
const auto &nameWords = row->history()->peer->nameWords();
|
const auto &nameWords = row->history()->peer->nameWords();
|
||||||
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
|
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
|
||||||
for (fi = fb; fi != fe; ++fi) {
|
for (fi = fb; fi != fe; ++fi) {
|
||||||
|
@ -1524,14 +1584,19 @@ void DialogsInner::clearSearchResults(bool clearPeerSearchResults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) {
|
PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) {
|
||||||
// #TODO feeds dialogs
|
|
||||||
_mouseSelection = true;
|
_mouseSelection = true;
|
||||||
updateSelected(mapFromGlobal(globalPos));
|
updateSelected(mapFromGlobal(globalPos));
|
||||||
|
const auto getPeerFromRow = [](Dialogs::Row *row) -> PeerData* {
|
||||||
|
if (const auto history = row ? row->history() : nullptr) {
|
||||||
|
return history->peer;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
if (_state == DefaultState) {
|
if (_state == DefaultState) {
|
||||||
if (_selected) return _selected->history()->peer;
|
return getPeerFromRow(_selected);
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) {
|
if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) {
|
||||||
return _filterResults[_filteredSelected]->history()->peer;
|
return getPeerFromRow(_filterResults[_filteredSelected]);
|
||||||
} else if (_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) {
|
} else if (_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) {
|
||||||
return _peerSearchResults[_peerSearchSelected]->peer;
|
return _peerSearchResults[_peerSearchSelected]->peer;
|
||||||
} else if (_searchedSelected >= 0 && _searchedSelected < _searchResults.size()) {
|
} else if (_searchedSelected >= 0 && _searchedSelected < _searchResults.size()) {
|
||||||
|
@ -1618,8 +1683,8 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) {
|
||||||
dialog.vnotify_settings,
|
dialog.vnotify_settings,
|
||||||
history);
|
history);
|
||||||
|
|
||||||
if (!history->isPinnedDialog() && !history->lastMsgDate.isNull()) {
|
if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) {
|
||||||
addSavedPeersAfter(history->lastMsgDate);
|
addSavedPeersAfter(history->chatsListDate());
|
||||||
}
|
}
|
||||||
_contactsNoDialogs->del(history);
|
_contactsNoDialogs->del(history);
|
||||||
if (peer->migrateFrom()) {
|
if (peer->migrateFrom()) {
|
||||||
|
@ -1654,9 +1719,12 @@ void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
|
||||||
feed->setUnreadCounts(
|
feed->setUnreadCounts(
|
||||||
dialog.vunread_count.v,
|
dialog.vunread_count.v,
|
||||||
dialog.vunread_muted_count.v);
|
dialog.vunread_muted_count.v);
|
||||||
|
if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) {
|
||||||
|
addSavedPeersAfter(feed->chatsListDate());
|
||||||
|
}
|
||||||
if (dialog.has_read_max_position()) {
|
if (dialog.has_read_max_position()) {
|
||||||
// #TODO feeds
|
const auto position = Data::FeedPosition(dialog.vread_max_position);
|
||||||
// feed->set
|
feed->setUnreadPosition(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2082,8 +2150,7 @@ void DialogsInner::loadPeerPhotos() {
|
||||||
if (((*i)->pos() * st::dialogsRowHeight) >= yTo) {
|
if (((*i)->pos() * st::dialogsRowHeight) >= yTo) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// #TODO feeds dialogs
|
(*i)->entry()->loadUserpic();
|
||||||
(*i)->history()->peer->loadUserpic();
|
|
||||||
}
|
}
|
||||||
yFrom = 0;
|
yFrom = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2098,8 +2165,7 @@ void DialogsInner::loadPeerPhotos() {
|
||||||
if (to > _filterResults.size()) to = _filterResults.size();
|
if (to > _filterResults.size()) to = _filterResults.size();
|
||||||
|
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
// #TODO feeds dialogs
|
_filterResults[from]->entry()->loadUserpic();
|
||||||
_filterResults[from]->history()->peer->loadUserpic();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,10 @@ public:
|
||||||
void selectSkip(int32 direction);
|
void selectSkip(int32 direction);
|
||||||
void selectSkipPage(int32 pixels, int32 direction);
|
void selectSkipPage(int32 pixels, int32 direction);
|
||||||
|
|
||||||
void createDialog(not_null<History*> history);
|
void createDialog(Dialogs::Key key);
|
||||||
|
void removeDialog(Dialogs::Key key);
|
||||||
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
|
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
|
||||||
void dlgUpdated(not_null<History*> history, MsgId msgId);
|
void dlgUpdated(not_null<History*> history, MsgId msgId);
|
||||||
void removeDialog(not_null<History*> history);
|
|
||||||
|
|
||||||
void dragLeft();
|
void dragLeft();
|
||||||
|
|
||||||
|
@ -207,8 +207,8 @@ private:
|
||||||
Painter &p,
|
Painter &p,
|
||||||
not_null<Dialogs::Row*> row,
|
not_null<Dialogs::Row*> row,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
PeerData *active,
|
Dialogs::Key active,
|
||||||
PeerData *selected,
|
Dialogs::Key selected,
|
||||||
bool onlyBackground,
|
bool onlyBackground,
|
||||||
TimeMs ms);
|
TimeMs ms);
|
||||||
void paintPeerSearchResult(
|
void paintPeerSearchResult(
|
||||||
|
@ -233,7 +233,7 @@ private:
|
||||||
|
|
||||||
void clearSelection();
|
void clearSelection();
|
||||||
void clearSearchResults(bool clearPeerSearchResults = true);
|
void clearSearchResults(bool clearPeerSearchResults = true);
|
||||||
void updateSelectedRow(PeerData *peer = 0);
|
void updateSelectedRow(Dialogs::Key key = Dialogs::Key());
|
||||||
|
|
||||||
Dialogs::IndexedList *shownDialogs() const {
|
Dialogs::IndexedList *shownDialogs() const {
|
||||||
return (Global::DialogsMode() == Dialogs::Mode::Important) ? _dialogsImportant.get() : _dialogs.get();
|
return (Global::DialogsMode() == Dialogs::Mode::Important) ? _dialogsImportant.get() : _dialogs.get();
|
||||||
|
@ -323,7 +323,7 @@ private:
|
||||||
UserData *_searchFromUser = nullptr;
|
UserData *_searchFromUser = nullptr;
|
||||||
Text _searchFromUserText;
|
Text _searchFromUserText;
|
||||||
Text _searchInSavedText;
|
Text _searchInSavedText;
|
||||||
PeerData *_menuPeer = nullptr;
|
Dialogs::Key _menuKey;
|
||||||
|
|
||||||
base::lambda<void()> _loadMoreCallback;
|
base::lambda<void()> _loadMoreCallback;
|
||||||
|
|
||||||
|
|
|
@ -29,24 +29,13 @@ const PeerData::NameFirstChars &Key::nameFirstChars() const {
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 Key::sortKey() const {
|
not_null<Entry*> Key::entry() const {
|
||||||
if (const auto h = history()) {
|
if (const auto p = base::get_if<not_null<History*>>(&_value)) {
|
||||||
return h->sortKeyInChatList();
|
return *p;
|
||||||
} else if (const auto f = feed()) {
|
} else if (const auto p = base::get_if<not_null<Data::Feed*>>(&_value)) {
|
||||||
return f->sortKeyInChatList();
|
return *p;
|
||||||
} else {
|
|
||||||
Unexpected("Key value in Key::sortKey");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Key::cachePinnedIndex(int index) const {
|
|
||||||
if (const auto h = history()) {
|
|
||||||
h->cachePinnedIndex(index);
|
|
||||||
} else if (const auto f = feed()) {
|
|
||||||
f->cachePinnedIndex(index);
|
|
||||||
} else {
|
|
||||||
Unexpected("Key value in Key::setPinnedIndex");
|
|
||||||
}
|
}
|
||||||
|
Unexpected("Dialogs entry() call on empty Key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
History *Key::history() const {
|
History *Key::history() const {
|
||||||
|
|
|
@ -9,12 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "base/value_ordering.h"
|
#include "base/value_ordering.h"
|
||||||
|
|
||||||
|
class History;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Feed;
|
class Feed;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
|
||||||
class Key {
|
class Key {
|
||||||
public:
|
public:
|
||||||
Key() = default;
|
Key() = default;
|
||||||
|
@ -30,13 +34,12 @@ public:
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return !!_value;
|
return !!_value;
|
||||||
}
|
}
|
||||||
|
not_null<Entry*> entry() const;
|
||||||
History *history() const;
|
History *history() const;
|
||||||
Data::Feed *feed() const;
|
Data::Feed *feed() const;
|
||||||
|
|
||||||
const QString &name() const;
|
const QString &name() const;
|
||||||
const PeerData::NameFirstChars &nameFirstChars() const;
|
const PeerData::NameFirstChars &nameFirstChars() const;
|
||||||
uint64 sortKey() const;
|
|
||||||
void cachePinnedIndex(int index) const;
|
|
||||||
|
|
||||||
inline bool operator<(const Key &other) const {
|
inline bool operator<(const Key &other) const {
|
||||||
return _value < other._value;
|
return _value < other._value;
|
||||||
|
|
|
@ -59,10 +59,11 @@ template <typename PaintItemCallback, typename PaintCounterCallback>
|
||||||
void paintRow(
|
void paintRow(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const RippleRow *row,
|
const RippleRow *row,
|
||||||
|
not_null<Entry*> entry,
|
||||||
History *history,
|
History *history,
|
||||||
not_null<PeerData*> from,
|
PeerData *from,
|
||||||
HistoryItem *item,
|
HistoryItem *item,
|
||||||
Data::Draft *draft,
|
const Data::Draft *draft,
|
||||||
QDateTime date,
|
QDateTime date,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
base::flags<Flag> flags,
|
base::flags<Flag> flags,
|
||||||
|
@ -94,13 +95,20 @@ void paintRow(
|
||||||
st::dialogsPadding.y(),
|
st::dialogsPadding.y(),
|
||||||
fullWidth,
|
fullWidth,
|
||||||
st::dialogsPhotoSize);
|
st::dialogsPhotoSize);
|
||||||
} else {
|
} else if (from) {
|
||||||
from->paintUserpicLeft(
|
from->paintUserpicLeft(
|
||||||
p,
|
p,
|
||||||
st::dialogsPadding.x(),
|
st::dialogsPadding.x(),
|
||||||
st::dialogsPadding.y(),
|
st::dialogsPadding.y(),
|
||||||
fullWidth,
|
fullWidth,
|
||||||
st::dialogsPhotoSize);
|
st::dialogsPhotoSize);
|
||||||
|
} else {
|
||||||
|
entry->paintUserpicLeft(
|
||||||
|
p,
|
||||||
|
st::dialogsPadding.x(),
|
||||||
|
st::dialogsPadding.y(),
|
||||||
|
fullWidth,
|
||||||
|
st::dialogsPhotoSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nameleft = st::dialogsPadding.x()
|
auto nameleft = st::dialogsPadding.x()
|
||||||
|
@ -120,11 +128,12 @@ void paintRow(
|
||||||
namewidth,
|
namewidth,
|
||||||
st::msgNameFont->height);
|
st::msgNameFont->height);
|
||||||
|
|
||||||
if (auto chatTypeIcon = ChatTypeIcon(from, active, selected)) {
|
if (from) {
|
||||||
chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth);
|
if (auto chatTypeIcon = ChatTypeIcon(from, active, selected)) {
|
||||||
rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip);
|
chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth);
|
||||||
|
rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto texttop = st::dialogsPadding.y()
|
auto texttop = st::dialogsPadding.y()
|
||||||
+ st::msgNameFont->height
|
+ st::msgNameFont->height
|
||||||
+ st::dialogsSkip;
|
+ st::dialogsSkip;
|
||||||
|
@ -132,7 +141,7 @@ void paintRow(
|
||||||
paintRowDate(p, date, rectForName, active, selected);
|
paintRowDate(p, date, rectForName, active, selected);
|
||||||
|
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
if (history->isPinnedDialog()) {
|
if (entry->isPinnedDialog()) {
|
||||||
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
|
@ -140,7 +149,7 @@ void paintRow(
|
||||||
|
|
||||||
p.setFont(st::dialogsTextFont);
|
p.setFont(st::dialogsTextFont);
|
||||||
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
||||||
if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
if (history && !history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||||
if (history->cloudDraftTextCache.isEmpty()) {
|
if (history->cloudDraftTextCache.isEmpty()) {
|
||||||
auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft)));
|
auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft)));
|
||||||
auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text));
|
auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text));
|
||||||
|
@ -153,7 +162,7 @@ void paintRow(
|
||||||
}
|
}
|
||||||
} else if (!item) {
|
} else if (!item) {
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
if (history->isPinnedDialog()) {
|
if (entry->isPinnedDialog()) {
|
||||||
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
|
@ -161,14 +170,14 @@ void paintRow(
|
||||||
|
|
||||||
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
||||||
p.setFont(st::dialogsTextFont);
|
p.setFont(st::dialogsTextFont);
|
||||||
if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
if (history && !history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||||
// Empty history
|
// Empty history
|
||||||
}
|
}
|
||||||
} else if (!item->isEmpty()) {
|
} else if (!item->isEmpty()) {
|
||||||
paintRowDate(p, date, rectForName, active, selected);
|
paintRowDate(p, date, rectForName, active, selected);
|
||||||
|
|
||||||
paintItemCallback(nameleft, namewidth);
|
paintItemCallback(nameleft, namewidth);
|
||||||
} else if (history->isPinnedDialog()) {
|
} else if (entry->isPinnedDialog()) {
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
|
@ -209,13 +218,22 @@ void paintRow(
|
||||||
text = st::msgNameFont->elided(text, rectForName.width());
|
text = st::msgNameFont->elided(text, rectForName.width());
|
||||||
}
|
}
|
||||||
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
|
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
|
||||||
} else {
|
} else if (from) {
|
||||||
if (!(flags & Flag::SearchResult) && from->isVerified()) {
|
if (!(flags & Flag::SearchResult) && from->isVerified()) {
|
||||||
auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon));
|
auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon));
|
||||||
rectForName.setWidth(rectForName.width() - icon->width());
|
rectForName.setWidth(rectForName.width() - icon->width());
|
||||||
icon->paint(p, rectForName.topLeft() + QPoint(qMin(from->dialogName().maxWidth(), rectForName.width()), 0), fullWidth);
|
icon->paint(p, rectForName.topLeft() + QPoint(qMin(from->dialogName().maxWidth(), rectForName.width()), 0), fullWidth);
|
||||||
}
|
}
|
||||||
from->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
|
from->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
|
||||||
|
} else {
|
||||||
|
// TODO feeds name
|
||||||
|
p.setFont(st::msgNameFont);
|
||||||
|
auto text = QString("Feed");
|
||||||
|
auto textWidth = st::msgNameFont->width(text);
|
||||||
|
if (textWidth > rectForName.width()) {
|
||||||
|
text = st::msgNameFont->elided(text, rectForName.width());
|
||||||
|
}
|
||||||
|
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,69 +346,79 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea
|
||||||
|
|
||||||
void RowPainter::paint(
|
void RowPainter::paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const Row *row,
|
not_null<const Row*> row,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
bool active,
|
bool active,
|
||||||
bool selected,
|
bool selected,
|
||||||
bool onlyBackground,
|
bool onlyBackground,
|
||||||
TimeMs ms) {
|
TimeMs ms) {
|
||||||
// #TODO feeds show
|
const auto entry = row->entry();
|
||||||
auto history = row->history();
|
const auto history = row->history();
|
||||||
auto item = history->lastMsg;
|
const auto peer = history ? history->peer : nullptr;
|
||||||
auto cloudDraft = history->cloudDraft();
|
const auto unreadCount = entry->chatListUnreadCount();
|
||||||
if (Data::draftIsNull(cloudDraft)) {
|
const auto unreadMuted = entry->chatListMutedBadge();
|
||||||
cloudDraft = nullptr;
|
const auto item = entry->chatsListItem();
|
||||||
}
|
const auto cloudDraft = [&]() -> const Data::Draft*{
|
||||||
auto displayDate = [item, cloudDraft]() {
|
if (history && (!item || !unreadCount)) {
|
||||||
|
// Draw item, if there are unread messages.
|
||||||
|
if (const auto draft = history->cloudDraft()) {
|
||||||
|
if (!Data::draftIsNull(draft)) {
|
||||||
|
return draft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
const auto displayDate = [item, cloudDraft] {
|
||||||
if (item) {
|
if (item) {
|
||||||
if (cloudDraft) {
|
if (cloudDraft) {
|
||||||
return (item->date > cloudDraft->date) ? item->date : cloudDraft->date;
|
return (item->date > cloudDraft->date)
|
||||||
|
? item->date
|
||||||
|
: cloudDraft->date;
|
||||||
}
|
}
|
||||||
return item->date;
|
return item->date;
|
||||||
}
|
}
|
||||||
return cloudDraft ? cloudDraft->date : QDateTime();
|
return cloudDraft ? cloudDraft->date : QDateTime();
|
||||||
};
|
}();
|
||||||
auto unreadCount = history->unreadCount();
|
|
||||||
if (history->peer->migrateFrom()) {
|
|
||||||
if (auto migrated = App::historyLoaded(history->peer->migrateFrom()->id)) {
|
|
||||||
unreadCount += migrated->unreadCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item && cloudDraft && unreadCount > 0) {
|
const auto from = history
|
||||||
cloudDraft = nullptr; // Draw item, if draft is older.
|
? (history->peer->migrateTo()
|
||||||
}
|
? history->peer->migrateTo()
|
||||||
auto from = history->peer->migrateTo()
|
: history->peer)
|
||||||
? history->peer->migrateTo()
|
: nullptr;
|
||||||
: history->peer;
|
|
||||||
const auto flags = (active ? Flag::Active : Flag(0))
|
const auto flags = (active ? Flag::Active : Flag(0))
|
||||||
| (selected ? Flag::Selected : Flag(0))
|
| (selected ? Flag::Selected : Flag(0))
|
||||||
| (onlyBackground ? Flag::OnlyBackground : Flag(0))
|
| (onlyBackground ? Flag::OnlyBackground : Flag(0))
|
||||||
| (history->peer->isSelf() ? Flag::SavedMessages : Flag(0));
|
| (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0));
|
||||||
auto paintItemCallback = [&](int nameleft, int namewidth) {
|
const auto paintItemCallback = [&](int nameleft, int namewidth) {
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip;
|
auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip;
|
||||||
auto hadOneBadge = false;
|
auto hadOneBadge = false;
|
||||||
auto displayUnreadCounter = (unreadCount != 0);
|
const auto displayMentionBadge = history
|
||||||
auto displayMentionBadge = history->hasUnreadMentions();
|
? history->hasUnreadMentions()
|
||||||
auto displayPinnedIcon = !displayUnreadCounter && history->isPinnedDialog();
|
: false;
|
||||||
if (displayMentionBadge
|
const auto displayUnreadCounter = [&] {
|
||||||
&& unreadCount == 1
|
if (displayMentionBadge
|
||||||
&& item
|
&& unreadCount == 1
|
||||||
&& item->isMediaUnread()
|
&& item
|
||||||
&& item->mentionsMe()) {
|
&& item->isMediaUnread()
|
||||||
displayUnreadCounter = false;
|
&& item->mentionsMe()) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
return (unreadCount > 0);
|
||||||
|
}();
|
||||||
|
const auto displayPinnedIcon = !displayUnreadCounter
|
||||||
|
&& !displayMentionBadge
|
||||||
|
&& entry->isPinnedDialog();
|
||||||
if (displayUnreadCounter) {
|
if (displayUnreadCounter) {
|
||||||
auto counter = QString::number(unreadCount);
|
auto counter = QString::number(unreadCount);
|
||||||
auto mutedCounter = history->mute();
|
|
||||||
auto unreadRight = fullWidth - st::dialogsPadding.x();
|
auto unreadRight = fullWidth - st::dialogsPadding.x();
|
||||||
auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2;
|
auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2;
|
||||||
auto unreadWidth = 0;
|
auto unreadWidth = 0;
|
||||||
|
|
||||||
UnreadBadgeStyle st;
|
UnreadBadgeStyle st;
|
||||||
st.active = active;
|
st.active = active;
|
||||||
st.muted = history->mute();
|
st.muted = unreadMuted;
|
||||||
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
|
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
|
||||||
availableWidth -= unreadWidth + st.padding;
|
availableWidth -= unreadWidth + st.padding;
|
||||||
|
|
||||||
|
@ -416,19 +444,19 @@ void RowPainter::paint(
|
||||||
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
|
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
|
||||||
availableWidth -= unreadWidth + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0);
|
availableWidth -= unreadWidth + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0);
|
||||||
}
|
}
|
||||||
auto &color = active
|
const auto &color = active
|
||||||
? st::dialogsTextFgServiceActive
|
? st::dialogsTextFgServiceActive
|
||||||
: (selected
|
: (selected
|
||||||
? st::dialogsTextFgServiceOver
|
? st::dialogsTextFgServiceOver
|
||||||
: st::dialogsTextFgService);
|
: st::dialogsTextFgService);
|
||||||
auto actionWasPainted = history->paintSendAction(
|
const auto actionWasPainted = history ? history->paintSendAction(
|
||||||
p,
|
p,
|
||||||
nameleft,
|
nameleft,
|
||||||
texttop,
|
texttop,
|
||||||
availableWidth,
|
availableWidth,
|
||||||
fullWidth,
|
fullWidth,
|
||||||
color,
|
color,
|
||||||
ms);
|
ms) : false;
|
||||||
if (!actionWasPainted) {
|
if (!actionWasPainted) {
|
||||||
auto itemRect = QRect(
|
auto itemRect = QRect(
|
||||||
nameleft,
|
nameleft,
|
||||||
|
@ -441,35 +469,35 @@ void RowPainter::paint(
|
||||||
active,
|
active,
|
||||||
selected,
|
selected,
|
||||||
HistoryItem::DrawInDialog::Normal,
|
HistoryItem::DrawInDialog::Normal,
|
||||||
history->textCachedFor,
|
entry->textCachedFor,
|
||||||
history->lastItemTextCache);
|
entry->lastItemTextCache);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto paintCounterCallback = [&] {
|
const auto paintCounterCallback = [&] {
|
||||||
if (unreadCount) {
|
if (unreadCount) {
|
||||||
auto counter = QString::number(unreadCount);
|
auto counter = QString::number(unreadCount);
|
||||||
if (counter.size() > 4) {
|
if (counter.size() > 4) {
|
||||||
counter = qsl("..") + counter.mid(counter.size() - 3);
|
counter = qsl("..") + counter.mid(counter.size() - 3);
|
||||||
}
|
}
|
||||||
auto mutedCounter = history->mute();
|
|
||||||
auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize;
|
auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize;
|
||||||
auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight;
|
auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight;
|
||||||
auto unreadWidth = 0;
|
auto unreadWidth = 0;
|
||||||
|
|
||||||
UnreadBadgeStyle st;
|
UnreadBadgeStyle st;
|
||||||
st.active = active;
|
st.active = active;
|
||||||
st.muted = history->mute();
|
st.muted = unreadMuted;
|
||||||
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
|
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
paintRow(
|
paintRow(
|
||||||
p,
|
p,
|
||||||
row,
|
row,
|
||||||
|
entry,
|
||||||
history,
|
history,
|
||||||
from,
|
from,
|
||||||
item,
|
item,
|
||||||
cloudDraft,
|
cloudDraft,
|
||||||
displayDate(),
|
displayDate,
|
||||||
fullWidth,
|
fullWidth,
|
||||||
flags,
|
flags,
|
||||||
ms,
|
ms,
|
||||||
|
@ -479,7 +507,7 @@ void RowPainter::paint(
|
||||||
|
|
||||||
void RowPainter::paint(
|
void RowPainter::paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const FakeRow *row,
|
not_null<const FakeRow*> row,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
bool active,
|
bool active,
|
||||||
bool selected,
|
bool selected,
|
||||||
|
@ -530,6 +558,7 @@ void RowPainter::paint(
|
||||||
p,
|
p,
|
||||||
row,
|
row,
|
||||||
history,
|
history,
|
||||||
|
history,
|
||||||
from,
|
from,
|
||||||
item,
|
item,
|
||||||
cloudDraft,
|
cloudDraft,
|
||||||
|
|
|
@ -23,7 +23,7 @@ class RowPainter {
|
||||||
public:
|
public:
|
||||||
static void paint(
|
static void paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const Row *row,
|
not_null<const Row*> row,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
bool active,
|
bool active,
|
||||||
bool selected,
|
bool selected,
|
||||||
|
@ -31,7 +31,7 @@ public:
|
||||||
TimeMs ms);
|
TimeMs ms);
|
||||||
static void paint(
|
static void paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const FakeRow *row,
|
not_null<const FakeRow*> row,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
bool active,
|
bool active,
|
||||||
bool selected,
|
bool selected,
|
||||||
|
|
|
@ -58,6 +58,9 @@ public:
|
||||||
Data::Feed *feed() const {
|
Data::Feed *feed() const {
|
||||||
return _id.feed();
|
return _id.feed();
|
||||||
}
|
}
|
||||||
|
not_null<Entry*> entry() const {
|
||||||
|
return _id.entry();
|
||||||
|
}
|
||||||
QString name() const {
|
QString name() const {
|
||||||
return _id.name();
|
return _id.name();
|
||||||
}
|
}
|
||||||
|
@ -65,7 +68,7 @@ public:
|
||||||
return _pos;
|
return _pos;
|
||||||
}
|
}
|
||||||
uint64 sortKey() const {
|
uint64 sortKey() const {
|
||||||
return _id.sortKey();
|
return _id.entry()->sortKeyInChatList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// for any attached data, for example View in contacts list
|
// for any attached data, for example View in contacts list
|
||||||
|
|
|
@ -182,11 +182,13 @@ void DialogsWidget::activate() {
|
||||||
_inner->activate();
|
_inner->activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsWidget::createDialog(not_null<History*> history) {
|
void DialogsWidget::createDialog(Dialogs::Key key) {
|
||||||
auto creating = !history->inChatList(Dialogs::Mode::All);
|
const auto creating = !key.entry()->inChatList(Dialogs::Mode::All);
|
||||||
_inner->createDialog(history);
|
_inner->createDialog(key);
|
||||||
if (creating && history->peer->migrateFrom()) {
|
const auto history = key.history();
|
||||||
if (const auto migrated = App::historyLoaded(history->peer->migrateFrom())) {
|
if (creating && history && history->peer->migrateFrom()) {
|
||||||
|
if (const auto migrated = App::historyLoaded(
|
||||||
|
history->peer->migrateFrom())) {
|
||||||
if (migrated->inChatList(Dialogs::Mode::All)) {
|
if (migrated->inChatList(Dialogs::Mode::All)) {
|
||||||
removeDialog(migrated);
|
removeDialog(migrated);
|
||||||
}
|
}
|
||||||
|
@ -747,8 +749,7 @@ void DialogsWidget::dragMoveEvent(QDragMoveEvent *e) {
|
||||||
} else {
|
} else {
|
||||||
_chooseByDragTimer.start(ChoosePeerByDragTimeout);
|
_chooseByDragTimer.start(ChoosePeerByDragTimeout);
|
||||||
}
|
}
|
||||||
PeerData *p = _inner->updateFromParentDrag(mapToGlobal(e->pos()));
|
if (_inner->updateFromParentDrag(mapToGlobal(e->pos()))) {
|
||||||
if (p) {
|
|
||||||
e->setDropAction(Qt::CopyAction);
|
e->setDropAction(Qt::CopyAction);
|
||||||
} else {
|
} else {
|
||||||
e->setDropAction(Qt::IgnoreAction);
|
e->setDropAction(Qt::IgnoreAction);
|
||||||
|
@ -1118,8 +1119,8 @@ void DialogsWidget::scrollToPeer(not_null<History*> history, MsgId msgId) {
|
||||||
_inner->scrollToPeer(history, msgId);
|
_inner->scrollToPeer(history, msgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsWidget::removeDialog(not_null<History*> history) {
|
void DialogsWidget::removeDialog(Dialogs::Key key) {
|
||||||
_inner->removeDialog(history);
|
_inner->removeDialog(key);
|
||||||
onFilterUpdate();
|
onFilterUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,8 @@ public:
|
||||||
|
|
||||||
void loadDialogs();
|
void loadDialogs();
|
||||||
void loadPinnedDialogs();
|
void loadPinnedDialogs();
|
||||||
void createDialog(not_null<History*> history);
|
void createDialog(Dialogs::Key key);
|
||||||
void removeDialog(not_null<History*> history);
|
void removeDialog(Dialogs::Key key);
|
||||||
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
|
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
|
||||||
void dlgUpdated(not_null<History*> history, MsgId msgId);
|
void dlgUpdated(not_null<History*> history, MsgId msgId);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/storage_facade.h"
|
#include "storage/storage_facade.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
#include "data/data_channel_admins.h"
|
#include "data/data_channel_admins.h"
|
||||||
|
#include "data/data_feed.h"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
#include "core/crash_reports.h"
|
#include "core/crash_reports.h"
|
||||||
|
|
||||||
|
@ -58,8 +59,8 @@ HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
History::History(const PeerId &peerId)
|
History::History(const PeerId &peerId)
|
||||||
: peer(App::peer(peerId))
|
: Entry(this)
|
||||||
, lastItemTextCache(st::dialogsTextWidthMin)
|
, peer(App::peer(peerId))
|
||||||
, cloudDraftTextCache(st::dialogsTextWidthMin)
|
, cloudDraftTextCache(st::dialogsTextWidthMin)
|
||||||
, _mute(peer->isMuted())
|
, _mute(peer->isMuted())
|
||||||
, _sendActionText(st::dialogsTextWidthMin) {
|
, _sendActionText(st::dialogsTextWidthMin) {
|
||||||
|
@ -450,9 +451,14 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
|
||||||
}
|
}
|
||||||
if (item->date <= inviteDate) {
|
if (item->date <= inviteDate) {
|
||||||
++itemIndex;
|
++itemIndex;
|
||||||
_joinedMessage = HistoryJoined::create(this, inviteDate, inviter, flags);
|
_joinedMessage = HistoryJoined::create(
|
||||||
|
this,
|
||||||
|
inviteDate,
|
||||||
|
inviter,
|
||||||
|
flags);
|
||||||
addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex);
|
addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex);
|
||||||
if (lastMsgDate.isNull() || inviteDate >= lastMsgDate) {
|
const auto lastDate = chatsListDate();
|
||||||
|
if (lastDate.isNull() || inviteDate >= lastDate) {
|
||||||
setLastMessage(_joinedMessage);
|
setLastMessage(_joinedMessage);
|
||||||
if (unread) {
|
if (unread) {
|
||||||
newItemAdded(_joinedMessage);
|
newItemAdded(_joinedMessage);
|
||||||
|
@ -1855,6 +1861,16 @@ void History::getNextShowFrom(HistoryBlock *block, int i) {
|
||||||
showFrom = nullptr;
|
showFrom = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDateTime History::adjustChatListDate() const {
|
||||||
|
const auto result = chatsListDate();
|
||||||
|
if (const auto draft = cloudDraft()) {
|
||||||
|
if (!Data::draftIsNull(draft) && draft->date > result) {
|
||||||
|
return draft->date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void History::countScrollState(int top) {
|
void History::countScrollState(int top) {
|
||||||
countScrollTopItem(top);
|
countScrollTopItem(top);
|
||||||
if (scrollTopItem) {
|
if (scrollTopItem) {
|
||||||
|
@ -2039,6 +2055,36 @@ void History::recountGroupingAround(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int History::chatListUnreadCount() const {
|
||||||
|
const auto result = unreadCount();
|
||||||
|
if (const auto from = peer->migrateFrom()) {
|
||||||
|
if (const auto migrated = App::historyLoaded(from)) {
|
||||||
|
return result + migrated->unreadCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool History::chatListMutedBadge() const {
|
||||||
|
return mute();
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem *History::chatsListItem() const {
|
||||||
|
return lastMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void History::loadUserpic() {
|
||||||
|
peer->loadUserpic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void History::paintUserpic(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int size) const {
|
||||||
|
peer->paintUserpic(p, x, y, size);
|
||||||
|
}
|
||||||
|
|
||||||
auto History::recountGroupingFromTill(not_null<HistoryItem*> item)
|
auto History::recountGroupingFromTill(not_null<HistoryItem*> item)
|
||||||
-> std::pair<not_null<HistoryItem*>, not_null<HistoryItem*>> {
|
-> std::pair<not_null<HistoryItem*>, not_null<HistoryItem*>> {
|
||||||
const auto recountFromItem = [&] {
|
const auto recountFromItem = [&] {
|
||||||
|
@ -2208,22 +2254,18 @@ uint32 _dialogsPosToTopShift = 0x80000000UL;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
inline uint64 dialogPosFromDate(const QDateTime &date) {
|
|
||||||
if (date.isNull()) return 0;
|
|
||||||
return (uint64(date.toTime_t()) << 32) | (++_dialogsPosToTopShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint64 pinnedDialogPos(int pinnedIndex) {
|
|
||||||
return 0xFFFFFFFF00000000ULL + pinnedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void History::setLastMessage(HistoryItem *msg) {
|
void History::setLastMessage(HistoryItem *msg) {
|
||||||
if (msg) {
|
if (msg) {
|
||||||
if (!lastMsg) Local::removeSavedPeer(peer);
|
if (!lastMsg) {
|
||||||
|
Local::removeSavedPeer(peer);
|
||||||
|
}
|
||||||
lastMsg = msg;
|
lastMsg = msg;
|
||||||
|
if (const auto feed = peer->feed()) {
|
||||||
|
feed->updateLastMessage(msg);
|
||||||
|
}
|
||||||
setChatsListDate(msg->date);
|
setChatsListDate(msg->date);
|
||||||
} else {
|
} else if (lastMsg) {
|
||||||
lastMsg = 0;
|
lastMsg = nullptr;
|
||||||
updateChatListEntry();
|
updateChatListEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2235,43 +2277,10 @@ bool History::needUpdateInChatList() const {
|
||||||
return false;
|
return false;
|
||||||
} else if (isPinnedDialog()) {
|
} else if (isPinnedDialog()) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
|
return !channel->feed() && channel->amIn();
|
||||||
}
|
}
|
||||||
return !peer->isChannel() || peer->asChannel()->amIn();
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
void History::setChatsListDate(const QDateTime &date) {
|
|
||||||
if (!lastMsgDate.isNull() && lastMsgDate >= date) {
|
|
||||||
if (!needUpdateInChatList() || !inChatList(Dialogs::Mode::All)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastMsgDate = date;
|
|
||||||
updateChatListSortPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
void History::updateChatListSortPosition() {
|
|
||||||
auto chatListDate = [this]() {
|
|
||||||
if (auto draft = cloudDraft()) {
|
|
||||||
if (!Data::draftIsNull(draft) && draft->date > lastMsgDate) {
|
|
||||||
return draft->date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lastMsgDate;
|
|
||||||
};
|
|
||||||
|
|
||||||
_sortKeyInChatList = isPinnedDialog()
|
|
||||||
? pinnedDialogPos(_pinnedIndex)
|
|
||||||
: dialogPosFromDate(chatListDate());
|
|
||||||
if (auto m = App::main()) {
|
|
||||||
if (needUpdateInChatList()) {
|
|
||||||
if (_sortKeyInChatList) {
|
|
||||||
m->createDialog(this);
|
|
||||||
updateChatListEntry();
|
|
||||||
} else {
|
|
||||||
m->deleteConversation(peer, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::fixLastMessage(bool wasAtBottom) {
|
void History::fixLastMessage(bool wasAtBottom) {
|
||||||
|
@ -2406,6 +2415,10 @@ void History::clear(bool leaveItems) {
|
||||||
setUnreadCount(0);
|
setUnreadCount(0);
|
||||||
if (auto channel = peer->asChannel()) {
|
if (auto channel = peer->asChannel()) {
|
||||||
channel->clearPinnedMessage();
|
channel->clearPinnedMessage();
|
||||||
|
if (const auto feed = channel->feed()) {
|
||||||
|
// Should be after setLastMessage(nullptr);
|
||||||
|
feed->historyCleared(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clearLastKeyboard();
|
clearLastKeyboard();
|
||||||
}
|
}
|
||||||
|
@ -2481,84 +2494,24 @@ void History::clearOnDestroy() {
|
||||||
clearBlocks(false);
|
clearBlocks(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
History::PositionInChatListChange History::adjustByPosInChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed) {
|
void History::removeDialog() {
|
||||||
Assert(indexed != nullptr);
|
if (const auto main = App::main()) {
|
||||||
Dialogs::Row *lnk = mainChatListLink(list);
|
main->deleteConversation(peer, false);
|
||||||
int32 movedFrom = lnk->pos();
|
|
||||||
indexed->adjustByPos(chatListLinks(list));
|
|
||||||
int32 movedTo = lnk->pos();
|
|
||||||
return { movedFrom, movedTo };
|
|
||||||
}
|
|
||||||
|
|
||||||
int History::posInChatList(Dialogs::Mode list) const {
|
|
||||||
return mainChatListLink(list)->pos();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialogs::Row *History::addToChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed) {
|
|
||||||
Assert(indexed != nullptr);
|
|
||||||
if (!inChatList(list)) {
|
|
||||||
chatListLinks(list) = indexed->addToEnd(this);
|
|
||||||
if (list == Dialogs::Mode::All && unreadCount()) {
|
|
||||||
App::histories().unreadIncrement(unreadCount(), mute());
|
|
||||||
Notify::unreadCounterUpdated();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mainChatListLink(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void History::removeFromChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed) {
|
|
||||||
Assert(indexed != nullptr);
|
|
||||||
if (inChatList(list)) {
|
|
||||||
indexed->del(this);
|
|
||||||
chatListLinks(list).clear();
|
|
||||||
if (list == Dialogs::Mode::All && unreadCount()) {
|
|
||||||
App::histories().unreadIncrement(-unreadCount(), mute());
|
|
||||||
Notify::unreadCounterUpdated();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::removeChatListEntryByLetter(Dialogs::Mode list, QChar letter) {
|
void History::changedInChatListHook(Dialogs::Mode list, bool added) {
|
||||||
Assert(letter != 0);
|
if (list == Dialogs::Mode::All && unreadCount()) {
|
||||||
if (inChatList(list)) {
|
const auto delta = added ? unreadCount() : -unreadCount();
|
||||||
chatListLinks(list).remove(letter);
|
App::histories().unreadIncrement(delta, mute());
|
||||||
|
Notify::unreadCounterUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::addChatListEntryByLetter(Dialogs::Mode list, QChar letter, Dialogs::Row *row) {
|
void History::changedChatListPinHook() {
|
||||||
Assert(letter != 0);
|
Notify::peerUpdatedDelayed(
|
||||||
if (inChatList(list)) {
|
peer,
|
||||||
chatListLinks(list).emplace(letter, row);
|
Notify::PeerUpdate::Flag::PinnedChanged);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void History::updateChatListEntry() const {
|
|
||||||
if (auto main = App::main()) {
|
|
||||||
if (inChatList(Dialogs::Mode::All)) {
|
|
||||||
main->dlgUpdated(
|
|
||||||
Dialogs::Mode::All,
|
|
||||||
mainChatListLink(Dialogs::Mode::All));
|
|
||||||
if (inChatList(Dialogs::Mode::Important)) {
|
|
||||||
main->dlgUpdated(
|
|
||||||
Dialogs::Mode::Important,
|
|
||||||
mainChatListLink(Dialogs::Mode::Important));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void History::cachePinnedIndex(int pinnedIndex) {
|
|
||||||
if (_pinnedIndex != pinnedIndex) {
|
|
||||||
auto wasPinned = isPinnedDialog();
|
|
||||||
_pinnedIndex = pinnedIndex;
|
|
||||||
updateChatListSortPosition();
|
|
||||||
updateChatListEntry();
|
|
||||||
if (wasPinned != isPinnedDialog()) {
|
|
||||||
Notify::peerUpdatedDelayed(
|
|
||||||
peer,
|
|
||||||
Notify::PeerUpdate::Flag::PinnedChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::changeMsgId(MsgId oldId, MsgId newId) {
|
void History::changeMsgId(MsgId oldId, MsgId newId) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "data/data_types.h"
|
#include "data/data_types.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "dialogs/dialogs_common.h"
|
#include "dialogs/dialogs_entry.h"
|
||||||
#include "ui/effects/send_action_animations.h"
|
#include "ui/effects/send_action_animations.h"
|
||||||
#include "base/observer.h"
|
#include "base/observer.h"
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
|
@ -162,7 +162,7 @@ class IndexedList;
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
||||||
class ChannelHistory;
|
class ChannelHistory;
|
||||||
class History {
|
class History : public Dialogs::Entry {
|
||||||
public:
|
public:
|
||||||
History(const PeerId &peerId);
|
History(const PeerId &peerId);
|
||||||
History(const History &) = delete;
|
History(const History &) = delete;
|
||||||
|
@ -244,32 +244,6 @@ public:
|
||||||
void setLastMessage(HistoryItem *msg);
|
void setLastMessage(HistoryItem *msg);
|
||||||
void fixLastMessage(bool wasAtBottom);
|
void fixLastMessage(bool wasAtBottom);
|
||||||
|
|
||||||
bool needUpdateInChatList() const;
|
|
||||||
void updateChatListSortPosition();
|
|
||||||
void setChatsListDate(const QDateTime &date);
|
|
||||||
uint64 sortKeyInChatList() const {
|
|
||||||
return _sortKeyInChatList;
|
|
||||||
}
|
|
||||||
struct PositionInChatListChange {
|
|
||||||
int movedFrom;
|
|
||||||
int movedTo;
|
|
||||||
};
|
|
||||||
PositionInChatListChange adjustByPosInChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed);
|
|
||||||
bool inChatList(Dialogs::Mode list) const {
|
|
||||||
return !chatListLinks(list).empty();
|
|
||||||
}
|
|
||||||
int posInChatList(Dialogs::Mode list) const;
|
|
||||||
Dialogs::Row *addToChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed);
|
|
||||||
void removeFromChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed);
|
|
||||||
void removeChatListEntryByLetter(Dialogs::Mode list, QChar letter);
|
|
||||||
void addChatListEntryByLetter(Dialogs::Mode list, QChar letter, Dialogs::Row *row);
|
|
||||||
void updateChatListEntry() const;
|
|
||||||
|
|
||||||
bool isPinnedDialog() const {
|
|
||||||
return (_pinnedIndex > 0);
|
|
||||||
}
|
|
||||||
void cachePinnedIndex(int newPinnedIndex);
|
|
||||||
|
|
||||||
MsgId minMsgId() const;
|
MsgId minMsgId() const;
|
||||||
MsgId maxMsgId() const;
|
MsgId maxMsgId() const;
|
||||||
MsgId msgIdForRead() const;
|
MsgId msgIdForRead() const;
|
||||||
|
@ -362,18 +336,17 @@ public:
|
||||||
bool newLoaded = true;
|
bool newLoaded = true;
|
||||||
HistoryItem *lastMsg = nullptr;
|
HistoryItem *lastMsg = nullptr;
|
||||||
HistoryItem *lastSentMsg = nullptr;
|
HistoryItem *lastSentMsg = nullptr;
|
||||||
QDateTime lastMsgDate;
|
|
||||||
|
|
||||||
typedef QList<HistoryItem*> NotifyQueue;
|
typedef QList<HistoryItem*> NotifyQueue;
|
||||||
NotifyQueue notifies;
|
NotifyQueue notifies;
|
||||||
|
|
||||||
Data::Draft *localDraft() {
|
Data::Draft *localDraft() const {
|
||||||
return _localDraft.get();
|
return _localDraft.get();
|
||||||
}
|
}
|
||||||
Data::Draft *cloudDraft() {
|
Data::Draft *cloudDraft() const {
|
||||||
return _cloudDraft.get();
|
return _cloudDraft.get();
|
||||||
}
|
}
|
||||||
Data::Draft *editDraft() {
|
Data::Draft *editDraft() const {
|
||||||
return _editDraft.get();
|
return _editDraft.get();
|
||||||
}
|
}
|
||||||
void setLocalDraft(std::unique_ptr<Data::Draft> &&draft);
|
void setLocalDraft(std::unique_ptr<Data::Draft> &&draft);
|
||||||
|
@ -397,6 +370,20 @@ public:
|
||||||
void setForwardDraft(MessageIdsList &&items);
|
void setForwardDraft(MessageIdsList &&items);
|
||||||
void recountGroupingAround(not_null<HistoryItem*> item);
|
void recountGroupingAround(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
bool needUpdateInChatList() const override;
|
||||||
|
bool toImportant() const override {
|
||||||
|
return !mute();
|
||||||
|
}
|
||||||
|
int chatListUnreadCount() const override;
|
||||||
|
bool chatListMutedBadge() const override;
|
||||||
|
HistoryItem *chatsListItem() const override;
|
||||||
|
void loadUserpic() override;
|
||||||
|
void paintUserpic(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int size) const override;
|
||||||
|
|
||||||
// some fields below are a property of a currently displayed instance of this
|
// some fields below are a property of a currently displayed instance of this
|
||||||
// conversation history not a property of the conversation history itself
|
// conversation history not a property of the conversation history itself
|
||||||
public:
|
public:
|
||||||
|
@ -436,9 +423,6 @@ public:
|
||||||
|
|
||||||
mtpRequestId sendRequestId = 0;
|
mtpRequestId sendRequestId = 0;
|
||||||
|
|
||||||
mutable const HistoryItem *textCachedFor = nullptr; // cache
|
|
||||||
mutable Text lastItemTextCache;
|
|
||||||
|
|
||||||
void changeMsgId(MsgId oldId, MsgId newId);
|
void changeMsgId(MsgId oldId, MsgId newId);
|
||||||
|
|
||||||
Text cloudDraftTextCache;
|
Text cloudDraftTextCache;
|
||||||
|
@ -482,6 +466,11 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QDateTime adjustChatListDate() const override;
|
||||||
|
void removeDialog() override;
|
||||||
|
void changedInChatListHook(Dialogs::Mode list, bool added) override;
|
||||||
|
void changedChatListPinHook() override;
|
||||||
|
|
||||||
// After adding a new history slice check the lastMsg and newLoaded.
|
// After adding a new history slice check the lastMsg and newLoaded.
|
||||||
void checkLastMsg();
|
void checkLastMsg();
|
||||||
|
|
||||||
|
@ -520,20 +509,6 @@ private:
|
||||||
base::optional<int> _unreadMentionsCount;
|
base::optional<int> _unreadMentionsCount;
|
||||||
base::flat_set<MsgId> _unreadMentions;
|
base::flat_set<MsgId> _unreadMentions;
|
||||||
|
|
||||||
Dialogs::RowsByLetter _chatListLinks[2];
|
|
||||||
Dialogs::RowsByLetter &chatListLinks(Dialogs::Mode list) {
|
|
||||||
return _chatListLinks[static_cast<int>(list)];
|
|
||||||
}
|
|
||||||
const Dialogs::RowsByLetter &chatListLinks(Dialogs::Mode list) const {
|
|
||||||
return _chatListLinks[static_cast<int>(list)];
|
|
||||||
}
|
|
||||||
Dialogs::Row *mainChatListLink(Dialogs::Mode list) const {
|
|
||||||
auto it = chatListLinks(list).find(0);
|
|
||||||
Assert(it != chatListLinks(list).cend());
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
uint64 _sortKeyInChatList = 0; // like ((unixtime) << 32) | (incremented counter)
|
|
||||||
|
|
||||||
// A pointer to the block that is currently being built.
|
// A pointer to the block that is currently being built.
|
||||||
// We hold this pointer so we can destroy it while building
|
// We hold this pointer so we can destroy it while building
|
||||||
// and then create a new one if it is necessary.
|
// and then create a new one if it is necessary.
|
||||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "core/crash_reports.h"
|
#include "core/crash_reports.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_feed.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ ReplyKeyboard *HistoryItem::inlineReplyKeyboard() {
|
||||||
|
|
||||||
void HistoryItem::invalidateChatsListEntry() {
|
void HistoryItem::invalidateChatsListEntry() {
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
// #TODO feeds dialogs
|
// #TODO feeds search results
|
||||||
App::main()->dlgUpdated(history(), id);
|
App::main()->dlgUpdated(history(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +173,12 @@ void HistoryItem::invalidateChatsListEntry() {
|
||||||
if (history()->textCachedFor == this) {
|
if (history()->textCachedFor == this) {
|
||||||
history()->textCachedFor = nullptr;
|
history()->textCachedFor = nullptr;
|
||||||
}
|
}
|
||||||
|
if (const auto feed = history()->peer->feed()) {
|
||||||
|
if (feed->textCachedFor == this) {
|
||||||
|
feed->textCachedFor = nullptr;
|
||||||
|
feed->updateChatListEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryItem::finishEditionToEmpty() {
|
void HistoryItem::finishEditionToEmpty() {
|
||||||
|
@ -280,6 +287,7 @@ UserData *HistoryItem::viaBot() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryItem::destroy() {
|
void HistoryItem::destroy() {
|
||||||
|
const auto history = this->history();
|
||||||
if (isLogEntry()) {
|
if (isLogEntry()) {
|
||||||
Assert(detached());
|
Assert(detached());
|
||||||
} else {
|
} else {
|
||||||
|
@ -288,7 +296,7 @@ void HistoryItem::destroy() {
|
||||||
if (IsServerMsgId(id)) {
|
if (IsServerMsgId(id)) {
|
||||||
if (const auto types = sharedMediaTypes()) {
|
if (const auto types = sharedMediaTypes()) {
|
||||||
Auth().storage().remove(Storage::SharedMediaRemoveOne(
|
Auth().storage().remove(Storage::SharedMediaRemoveOne(
|
||||||
history()->peer->id,
|
history->peer->id,
|
||||||
types,
|
types,
|
||||||
id));
|
id));
|
||||||
}
|
}
|
||||||
|
@ -296,22 +304,30 @@ void HistoryItem::destroy() {
|
||||||
Auth().api().cancelLocalItem(this);
|
Auth().api().cancelLocalItem(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wasAtBottom = history()->loadedAtBottom();
|
const auto wasAtBottom = history->loadedAtBottom();
|
||||||
_history->removeNotification(this);
|
history->removeNotification(this);
|
||||||
|
|
||||||
detach();
|
detach();
|
||||||
if (const auto channel = history()->peer->asChannel()) {
|
|
||||||
|
if (history->lastMsg == this) {
|
||||||
|
history->fixLastMessage(wasAtBottom);
|
||||||
|
}
|
||||||
|
if (history->lastKeyboardId == id) {
|
||||||
|
history->clearLastKeyboard();
|
||||||
|
}
|
||||||
|
if ((!out() || isPost()) && unread() && history->unreadCount() > 0) {
|
||||||
|
history->setUnreadCount(history->unreadCount() - 1);
|
||||||
|
}
|
||||||
|
if (const auto channel = history->peer->asChannel()) {
|
||||||
if (channel->pinnedMessageId() == id) {
|
if (channel->pinnedMessageId() == id) {
|
||||||
channel->clearPinnedMessage();
|
channel->clearPinnedMessage();
|
||||||
}
|
}
|
||||||
}
|
if (const auto feed = channel->feed()) {
|
||||||
if (history()->lastMsg == this) {
|
// Must be after histroy->lastMsg is cleared.
|
||||||
history()->fixLastMessage(wasAtBottom);
|
// Otherwise feed last message will be this value again.
|
||||||
}
|
feed->messageRemoved(this);
|
||||||
if (history()->lastKeyboardId == id) {
|
// #TODO feeds unread
|
||||||
history()->clearLastKeyboard();
|
}
|
||||||
}
|
|
||||||
if ((!out() || isPost()) && unread() && history()->unreadCount() > 0) {
|
|
||||||
history()->setUnreadCount(history()->unreadCount() - 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Global::RefPendingRepaintItems().remove(this);
|
Global::RefPendingRepaintItems().remove(this);
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "data/data_feed.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
|
@ -774,8 +775,14 @@ void HistoryService::updateDependentText() {
|
||||||
if (history()->textCachedFor == this) {
|
if (history()->textCachedFor == this) {
|
||||||
history()->textCachedFor = nullptr;
|
history()->textCachedFor = nullptr;
|
||||||
}
|
}
|
||||||
|
if (const auto feed = history()->peer->feed()) {
|
||||||
|
if (feed->textCachedFor == this) {
|
||||||
|
feed->textCachedFor = nullptr;
|
||||||
|
feed->updateChatListEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
// #TODO feeds dialogs
|
// #TODO feeds search results
|
||||||
App::main()->dlgUpdated(history(), id);
|
App::main()->dlgUpdated(history(), id);
|
||||||
}
|
}
|
||||||
App::historyUpdateDependent(this);
|
App::historyUpdateDependent(this);
|
||||||
|
|
|
@ -1091,8 +1091,8 @@ void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result)
|
||||||
App::feedUserLink(MTP_int(peerToUser(user->id)), d.vmy_link, d.vforeign_link);
|
App::feedUserLink(MTP_int(peerToUser(user->id)), d.vmy_link, d.vforeign_link);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::removeDialog(not_null<History*> history) {
|
void MainWidget::removeDialog(Dialogs::Key key) {
|
||||||
_dialogs->removeDialog(history);
|
_dialogs->removeDialog(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::deleteConversation(PeerData *peer, bool deleteHistory) {
|
void MainWidget::deleteConversation(PeerData *peer, bool deleteHistory) {
|
||||||
|
@ -1306,31 +1306,36 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
|
||||||
|
|
||||||
if (!v || v->isEmpty()) {
|
if (!v || v->isEmpty()) {
|
||||||
if (peer->isChat() && !peer->asChat()->haveLeft()) {
|
if (peer->isChat() && !peer->asChat()->haveLeft()) {
|
||||||
auto h = App::historyLoaded(peer->id);
|
if (const auto history = App::historyLoaded(peer->id)) {
|
||||||
if (h) Local::addSavedPeer(peer, h->lastMsgDate);
|
Local::addSavedPeer(peer, history->chatsListDate());
|
||||||
} else if (peer->isChannel()) {
|
}
|
||||||
if (peer->asChannel()->inviter > 0 && peer->asChannel()->amIn()) {
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
if (auto from = App::userLoaded(peer->asChannel()->inviter)) {
|
if (channel->inviter > 0 && channel->amIn()) {
|
||||||
auto h = App::history(peer->id);
|
if (const auto from = App::userLoaded(channel->inviter)) {
|
||||||
h->clear(true);
|
const auto history = App::history(peer->id);
|
||||||
h->addNewerSlice(QVector<MTPMessage>());
|
history->clear(true);
|
||||||
h->asChannelHistory()->insertJoinedMessage(true);
|
history->addNewerSlice(QVector<MTPMessage>());
|
||||||
_history->peerMessagesUpdated(h->peer->id);
|
history->asChannelHistory()->insertJoinedMessage(true);
|
||||||
|
_history->peerMessagesUpdated(peer->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deleteConversation(peer, false);
|
deleteConversation(peer, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto h = App::history(peer->id);
|
const auto history = App::history(peer->id);
|
||||||
if (!h->lastMsg) {
|
if (!history->lastMsg) {
|
||||||
h->addNewMessage((*v)[0], NewMessageLast);
|
history->addNewMessage((*v)[0], NewMessageLast);
|
||||||
}
|
}
|
||||||
if (!h->lastMsgDate.isNull() && h->loadedAtBottom()) {
|
if (!history->chatsListDate().isNull() && history->loadedAtBottom()) {
|
||||||
if (peer->isChannel() && peer->asChannel()->inviter > 0 && h->lastMsgDate <= peer->asChannel()->inviteDate && peer->asChannel()->amIn()) {
|
if (const auto channel = peer->asChannel()) {
|
||||||
if (auto from = App::userLoaded(peer->asChannel()->inviter)) {
|
if (channel->inviter > 0
|
||||||
h->asChannelHistory()->insertJoinedMessage(true);
|
&& history->chatsListDate() <= channel->inviteDate
|
||||||
_history->peerMessagesUpdated(h->peer->id);
|
&& channel->amIn()) {
|
||||||
|
if (const auto from = App::userLoaded(channel->inviter)) {
|
||||||
|
history->asChannelHistory()->insertJoinedMessage(true);
|
||||||
|
_history->peerMessagesUpdated(peer->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2200,8 +2205,8 @@ bool MainWidget::viewsIncrementFail(const RPCError &error, mtpRequestId req) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::createDialog(not_null<History*> history) {
|
void MainWidget::createDialog(Dialogs::Key key) {
|
||||||
_dialogs->createDialog(history);
|
_dialogs->createDialog(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::choosePeer(PeerId peerId, MsgId showAtMsgId) {
|
void MainWidget::choosePeer(PeerId peerId, MsgId showAtMsgId) {
|
||||||
|
|
|
@ -106,8 +106,8 @@ public:
|
||||||
|
|
||||||
void activate();
|
void activate();
|
||||||
|
|
||||||
void createDialog(not_null<History*> history);
|
void createDialog(Dialogs::Key key);
|
||||||
void removeDialog(not_null<History*> history);
|
void removeDialog(Dialogs::Key key);
|
||||||
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
|
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
|
||||||
void dlgUpdated(not_null<History*> history, MsgId msgId);
|
void dlgUpdated(not_null<History*> history, MsgId msgId);
|
||||||
|
|
||||||
|
|
|
@ -261,8 +261,8 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
|
||||||
return history->blocks.front()->items.front()->date.date();
|
return history->blocks.front()->items.front()->date.date();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!history->lastMsgDate.isNull()) {
|
} else if (!history->chatsListDate().isNull()) {
|
||||||
return history->lastMsgDate.date();
|
return history->chatsListDate().date();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QDate::currentDate();
|
return QDate::currentDate();
|
||||||
|
@ -272,8 +272,8 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
|
||||||
peer = channel;
|
peer = channel;
|
||||||
}
|
}
|
||||||
if (auto history = App::historyLoaded(peer)) {
|
if (auto history = App::historyLoaded(peer)) {
|
||||||
if (!history->lastMsgDate.isNull()) {
|
if (!history->chatsListDate().isNull()) {
|
||||||
return history->lastMsgDate.date();
|
return history->chatsListDate().date();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QDate::currentDate();
|
return QDate::currentDate();
|
||||||
|
|
|
@ -25,18 +25,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_feed.h"
|
||||||
|
#include "dialogs/dialogs_key.h"
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void AddChatMembers(not_null<ChatData*> chat) {
|
|
||||||
if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
|
|
||||||
Ui::show(Box<ConvertToSupergroupBox>(chat));
|
|
||||||
} else {
|
|
||||||
AddParticipantsBoxController::Start(chat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Filler {
|
class Filler {
|
||||||
public:
|
public:
|
||||||
Filler(
|
Filler(
|
||||||
|
@ -64,6 +58,25 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FeedFiller {
|
||||||
|
public:
|
||||||
|
FeedFiller(
|
||||||
|
not_null<Controller*> controller,
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
const PeerMenuCallback &addAction,
|
||||||
|
PeerMenuSource source);
|
||||||
|
void fill();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addPinToggle();
|
||||||
|
|
||||||
|
not_null<Controller*> _controller;
|
||||||
|
not_null<Data::Feed*> _feed;
|
||||||
|
const PeerMenuCallback &_addAction;
|
||||||
|
PeerMenuSource _source;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
History *FindWastedPin() {
|
History *FindWastedPin() {
|
||||||
const auto &order = Auth().data().pinnedDialogsOrder();
|
const auto &order = Auth().data().pinnedDialogsOrder();
|
||||||
for (const auto pinned : order) {
|
for (const auto pinned : order) {
|
||||||
|
@ -78,6 +91,58 @@ History *FindWastedPin() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddChatMembers(not_null<ChatData*> chat) {
|
||||||
|
if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
|
||||||
|
Ui::show(Box<ConvertToSupergroupBox>(chat));
|
||||||
|
} else {
|
||||||
|
AddParticipantsBoxController::Start(chat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PinnedLimitReached(Dialogs::Key key) {
|
||||||
|
const auto pinnedCount = Auth().data().pinnedDialogsCount();
|
||||||
|
const auto pinnedMax = Global::PinnedDialogsCountMax();
|
||||||
|
if (pinnedCount < pinnedMax) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Some old chat, that was converted, maybe is still pinned.
|
||||||
|
if (auto wasted = FindWastedPin()) {
|
||||||
|
Auth().data().setPinnedDialog(wasted, false);
|
||||||
|
Auth().data().setPinnedDialog(key, true);
|
||||||
|
Auth().api().savePinnedOrder();
|
||||||
|
} else {
|
||||||
|
auto errorText = lng_error_pinned_max(
|
||||||
|
lt_count,
|
||||||
|
pinnedMax);
|
||||||
|
Ui::show(Box<InformBox>(errorText));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TogglePinnedDialog(Dialogs::Key key) {
|
||||||
|
const auto isPinned = !key.entry()->isPinnedDialog();
|
||||||
|
if (isPinned && PinnedLimitReached(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Auth().data().setPinnedDialog(key, isPinned);
|
||||||
|
auto flags = MTPmessages_ToggleDialogPin::Flags(0);
|
||||||
|
if (isPinned) {
|
||||||
|
flags |= MTPmessages_ToggleDialogPin::Flag::f_pinned;
|
||||||
|
}
|
||||||
|
MTP::send(MTPmessages_ToggleDialogPin(
|
||||||
|
MTP_flags(flags),
|
||||||
|
key.history()
|
||||||
|
? MTP_inputDialogPeer(key.history()->peer->input)
|
||||||
|
: MTP_inputDialogPeerFeed(MTP_int(key.feed()->id()))));
|
||||||
|
if (isPinned) {
|
||||||
|
if (const auto main = App::main()) {
|
||||||
|
main->dialogsToUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Filler::Filler(
|
Filler::Filler(
|
||||||
not_null<Controller*> controller,
|
not_null<Controller*> controller,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
@ -115,40 +180,8 @@ void Filler::addPinToggle() {
|
||||||
? lng_context_unpin_from_top
|
? lng_context_unpin_from_top
|
||||||
: lng_context_pin_to_top);
|
: lng_context_pin_to_top);
|
||||||
};
|
};
|
||||||
auto pinToggle = [peer] {
|
auto pinToggle = [=] {
|
||||||
auto history = App::history(peer);
|
TogglePinnedDialog(App::history(peer));
|
||||||
auto isPinned = !history->isPinnedDialog();
|
|
||||||
const auto pinnedCount = Auth().data().pinnedDialogsCount();
|
|
||||||
const auto pinnedMax = Global::PinnedDialogsCountMax();
|
|
||||||
if (isPinned && pinnedCount >= pinnedMax) {
|
|
||||||
// Some old chat, that was converted to supergroup, maybe is still pinned.
|
|
||||||
if (auto wasted = FindWastedPin()) {
|
|
||||||
Auth().data().setPinnedDialog(wasted, false);
|
|
||||||
Auth().data().setPinnedDialog(history, true);
|
|
||||||
Auth().api().savePinnedOrder();
|
|
||||||
} else {
|
|
||||||
auto errorText = lng_error_pinned_max(
|
|
||||||
lt_count,
|
|
||||||
pinnedMax);
|
|
||||||
Ui::show(Box<InformBox>(errorText));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Auth().data().setPinnedDialog(history, isPinned);
|
|
||||||
auto flags = MTPmessages_ToggleDialogPin::Flags(0);
|
|
||||||
if (isPinned) {
|
|
||||||
flags |= MTPmessages_ToggleDialogPin::Flag::f_pinned;
|
|
||||||
}
|
|
||||||
MTP::send(
|
|
||||||
MTPmessages_ToggleDialogPin(
|
|
||||||
MTP_flags(flags),
|
|
||||||
MTP_inputDialogPeer(peer->input)));
|
|
||||||
if (isPinned) {
|
|
||||||
if (auto main = App::main()) {
|
|
||||||
main->dialogsToUp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
auto pinAction = _addAction(pinText(isPinned), pinToggle);
|
auto pinAction = _addAction(pinText(isPinned), pinToggle);
|
||||||
|
|
||||||
|
@ -382,6 +415,37 @@ void Filler::fill() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FeedFiller::FeedFiller(
|
||||||
|
not_null<Controller*> controller,
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
const PeerMenuCallback &addAction,
|
||||||
|
PeerMenuSource source)
|
||||||
|
: _controller(controller)
|
||||||
|
, _feed(feed)
|
||||||
|
, _addAction(addAction)
|
||||||
|
, _source(source) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedFiller::fill() {
|
||||||
|
if (_source == PeerMenuSource::ChatsList) {
|
||||||
|
addPinToggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedFiller::addPinToggle() {
|
||||||
|
auto feed = _feed;
|
||||||
|
auto isPinned = feed->isPinnedDialog();
|
||||||
|
auto pinText = [](bool isPinned) {
|
||||||
|
return lang(isPinned
|
||||||
|
? lng_context_unpin_from_top
|
||||||
|
: lng_context_pin_to_top);
|
||||||
|
};
|
||||||
|
auto pinToggle = [=] {
|
||||||
|
TogglePinnedDialog(feed);
|
||||||
|
};
|
||||||
|
_addAction(pinText(isPinned), pinToggle);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void PeerMenuDeleteContact(not_null<UserData*> user) {
|
void PeerMenuDeleteContact(not_null<UserData*> user) {
|
||||||
|
@ -599,4 +663,14 @@ void FillPeerMenu(
|
||||||
filler.fill();
|
filler.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillFeedMenu(
|
||||||
|
not_null<Controller*> controller,
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
const PeerMenuCallback &callback,
|
||||||
|
PeerMenuSource source) {
|
||||||
|
// TODO feeds context menu
|
||||||
|
FeedFiller filler(controller, feed, callback, source);
|
||||||
|
filler.fill();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -11,6 +11,10 @@ namespace Ui {
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class Feed;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
||||||
class Controller;
|
class Controller;
|
||||||
|
@ -30,6 +34,11 @@ void FillPeerMenu(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const PeerMenuCallback &addAction,
|
const PeerMenuCallback &addAction,
|
||||||
PeerMenuSource source);
|
PeerMenuSource source);
|
||||||
|
void FillFeedMenu(
|
||||||
|
not_null<Controller*> controller,
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
const PeerMenuCallback &addAction,
|
||||||
|
PeerMenuSource source);
|
||||||
|
|
||||||
void PeerMenuDeleteContact(not_null<UserData*> user);
|
void PeerMenuDeleteContact(not_null<UserData*> user);
|
||||||
void PeerMenuShareContactBox(not_null<UserData*> user);
|
void PeerMenuShareContactBox(not_null<UserData*> user);
|
||||||
|
|
|
@ -193,7 +193,8 @@
|
||||||
<(src_loc)/data/data_user_photos.h
|
<(src_loc)/data/data_user_photos.h
|
||||||
<(src_loc)/data/data_web_page.cpp
|
<(src_loc)/data/data_web_page.cpp
|
||||||
<(src_loc)/data/data_web_page.h
|
<(src_loc)/data/data_web_page.h
|
||||||
<(src_loc)/dialogs/dialogs_common.h
|
<(src_loc)/dialogs/dialogs_entry.cpp
|
||||||
|
<(src_loc)/dialogs/dialogs_entry.h
|
||||||
<(src_loc)/dialogs/dialogs_indexed_list.cpp
|
<(src_loc)/dialogs/dialogs_indexed_list.cpp
|
||||||
<(src_loc)/dialogs/dialogs_indexed_list.h
|
<(src_loc)/dialogs/dialogs_indexed_list.h
|
||||||
<(src_loc)/dialogs/dialogs_inner_widget.cpp
|
<(src_loc)/dialogs/dialogs_inner_widget.cpp
|
||||||
|
|
Loading…
Reference in New Issue