Add ability to archive/unarchive the chats.

This commit is contained in:
John Preston 2019-04-18 12:28:43 +04:00
parent c58f097535
commit 854870683b
17 changed files with 210 additions and 129 deletions

View file

@ -1192,10 +1192,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_mute" = "Mute";
"lng_channel_unmute" = "Unmute";
"lng_saved_messages" = "Saved Messages";
"lng_archived_chats" = "Archived chats";
"lng_saved_short" = "Save";
"lng_saved_forward_here" = "Forward messages here for quick access";
"lng_archived_chats" = "Archived chats";
"lng_archived_add" = "Archive";
"lng_archived_remove" = "Unarchive";
"lng_chat_archived" = "Chat archived.\nMuted chats will stay archived after new messages arrive.";
"lng_chat_unarchived" = "Chat restored from your archive.";
"lng_dialogs_text_with_from" = "{from_part} {message}";
"lng_dialogs_text_from_wrapped" = "{from}:";
"lng_dialogs_text_media" = "{media_part} {caption}";

View file

@ -460,29 +460,27 @@ void ApiWrap::toggleHistoryArchived(
if (const auto already = _historyArchivedRequests.take(history)) {
request(already->first).cancel();
}
// #TODO archive
const auto folderId = Data::Folder::kId;
//const auto flags = group
// ? MTPchannels_ChangeFeedBroadcast::Flag::f_feed_id
// : MTPchannels_ChangeFeedBroadcast::Flag(0);
//const auto requestId = request(MTPchannels_ChangeFeedBroadcast(
// MTP_flags(flags),
// channel->inputChannel,
// MTP_int(feedId)
//)).done([=](const MTPUpdates &result) {
// applyUpdates(result);
// if (group) {
// channel->setFeed(_session->data().feed(feedId));
// } else {
// channel->clearFeed();
// }
// if (const auto data = _channelGroupingRequests.take(channel)) {
// data->second();
// }
//}).fail([=](const RPCError &error) {
// _channelGroupingRequests.remove(channel);
//}).send();
//_channelGroupingRequests.emplace(channel, requestId, callback);
const auto archiveId = Data::Folder::kId;
const auto requestId = request(MTPfolders_EditPeerFolders(
MTP_vector<MTPInputFolderPeer>(
1,
MTP_inputFolderPeer(
history->peer->input,
MTP_int(archived ? archiveId : 0)))
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
if (archived) {
history->setFolder(_session->data().folder(archiveId));
} else {
history->clearFolder();
}
if (const auto data = _historyArchivedRequests.take(history)) {
data->second();
}
}).fail([=](const RPCError &error) {
_historyArchivedRequests.remove(history);
}).send();
_historyArchivedRequests.emplace(history, requestId, callback);
}
// #feed
//void ApiWrap::ungroupAllFromFeed(not_null<Data::Feed*> feed) {
@ -678,7 +676,7 @@ QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
}
void ApiWrap::requestContacts() {
if (_session->data().contactsLoaded().value() || _contactsRequestId) {
if (_session->data().contactsLoaded().current() || _contactsRequestId) {
return;
}
_contactsRequestId = request(MTPcontacts_GetContacts(
@ -700,7 +698,7 @@ void ApiWrap::requestContacts() {
UserData::ContactStatus::Contact);
}
}
_session->data().contactsLoaded().set(true);
_session->data().contactsLoaded() = true;
}).fail([=](const RPCError &error) {
_contactsRequestId = 0;
}).send();
@ -745,10 +743,10 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
: MTP_inputPeerEmpty()),
MTP_int(loadCount),
MTP_int(hash)
)).done([=](const MTPmessages_Dialogs &result) {
)).done([=](const MTPmessages_Dialogs & result) {
result.match([](const MTPDmessages_dialogsNotModified & data) {
LOG(("API Error: not-modified received for requested dialogs."));
}, [&](const auto &data) {
}, [&](const auto & data) {
if constexpr (data.Is<MTPDmessages_dialogs>()) {
dialogsLoadFinish(folderId);
} else {
@ -767,17 +765,12 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
if (!folderId) {
requestDialogs();
requestContacts();
if (!_dialogsLoadState || !_dialogsLoadState->requestId) {
refreshDialogsLoadBlocked();
}
}
_session->data().moreChatsLoaded().notify();
if (!folderId) {
if (!_dialogsLoadState && _pinnedDialogsReceived) {
_session->data().allChatsLoaded().set(true);
}
requestContacts();
}
_session->data().chatsListChanged(folderId);
}).fail([=](const RPCError &error) {
dialogsLoadState(folderId)->requestId = 0;
}).send();
@ -852,11 +845,19 @@ auto ApiWrap::dialogsLoadState(FolderId folderId) -> DialogsLoadState* {
}
void ApiWrap::dialogsLoadFinish(FolderId folderId) {
const auto notify = [&] {
Core::App().postponeCall(crl::guard(_session, [=] {
_session->data().chatsListDone(folderId);
}));
};
if (folderId) {
_session->data().folder(folderId)->setChatsListLoaded(true);
_foldersLoadState.remove(folderId);
notify();
} else {
_dialogsLoadState = nullptr;
if (_pinnedDialogsReceived) {
notify();
}
}
}
@ -865,7 +866,6 @@ void ApiWrap::requestPinnedDialogs() {
return;
}
_pinnedDialogsReceived = false;
const auto folderId = FolderId(0);
_pinnedDialogsRequestId = request(MTPmessages_GetPinnedDialogs(
MTP_int(folderId)
@ -884,9 +884,9 @@ void ApiWrap::requestPinnedDialogs() {
_pinnedDialogsRequestId = 0;
_pinnedDialogsReceived = true;
_session->data().moreChatsLoaded().notify();
_session->data().chatsListChanged(folderId);
if (!_dialogsLoadState) {
_session->data().allChatsLoaded().set(true);
_session->data().chatsListDone(folderId);
}
});
}).fail([=](const RPCError &error) {

View file

@ -225,18 +225,26 @@ void ChatsListBoxController::prepare() {
prepareViewHook();
rebuildRows();
if (!Auth().data().chatsListLoaded()) {
Auth().data().chatsListLoadedEvents(
) | rpl::filter([=](Data::Folder *folder) {
return !folder;
}) | rpl::start_with_next([=] {
checkForEmptyRows();
}, lifetime());
}
auto &sessionData = Auth().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
Auth().data().chatsListChanges(
) | rpl::filter([=](Data::Folder *folder) {
return !folder;
}) | rpl::start_with_next([=] {
rebuildRows();
});
subscribe(sessionData.moreChatsLoaded(), [this] {
}, lifetime());
Auth().data().contactsLoaded().value(
) | rpl::start_with_next([=] {
rebuildRows();
});
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
checkForEmptyRows();
});
}, lifetime());
}
void ChatsListBoxController::rebuildRows() {
@ -280,8 +288,8 @@ void ChatsListBoxController::checkForEmptyRows() {
if (delegate()->peerListFullRowsCount()) {
setDescriptionText(QString());
} else {
auto &sessionData = Auth().data();
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
const auto loaded = Auth().data().contactsLoaded().current()
&& Auth().data().chatsListLoaded();
setDescriptionText(loaded ? emptyBoxText() : lang(lng_contacts_loading));
}
}
@ -318,12 +326,10 @@ void ContactsBoxController::prepare() {
prepareViewHook();
rebuildRows();
auto &sessionData = Auth().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
Auth().data().contactsLoaded().value(
) | rpl::start_with_next([=] {
rebuildRows();
});
}, lifetime());
}
void ContactsBoxController::rebuildRows() {
@ -349,8 +355,7 @@ void ContactsBoxController::checkForEmptyRows() {
if (delegate()->peerListFullRowsCount()) {
setDescriptionText(QString());
} else {
auto &sessionData = Auth().data();
auto loaded = sessionData.contactsLoaded().value();
const auto loaded = Auth().data().contactsLoaded().current();
setDescriptionText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
}
}
@ -473,13 +478,13 @@ bool AddBotToGroupBoxController::sharingBotGame() const {
}
QString AddBotToGroupBoxController::emptyBoxText() const {
return lang(Auth().data().allChatsLoaded().value()
return lang(Auth().data().chatsListLoaded()
? (sharingBotGame() ? lng_bot_no_chats : lng_bot_no_groups)
: lng_contacts_loading);
}
QString AddBotToGroupBoxController::noResultsText() const {
return lang(Auth().data().allChatsLoaded().value()
return lang(Auth().data().chatsListLoaded()
? (sharingBotGame() ? lng_bot_chats_not_found : lng_bot_groups_not_found)
: lng_contacts_loading);
}
@ -493,7 +498,12 @@ void AddBotToGroupBoxController::prepareViewHook() {
? lng_bot_choose_chat
: lng_bot_choose_group));
updateLabels();
subscribe(Auth().data().allChatsLoaded(), [this](bool) { updateLabels(); });
Auth().data().chatsListLoadedEvents(
) | rpl::filter([=](Data::Folder *folder) {
return !folder;
}) | rpl::start_with_next([=] {
updateLabels();
}, lifetime());
}
ChooseRecipientBoxController::ChooseRecipientBoxController(

View file

@ -76,9 +76,7 @@ private:
};
class ChatsListBoxController
: public PeerListController
, protected base::Subscriber {
class ChatsListBoxController : public PeerListController {
public:
ChatsListBoxController(
std::unique_ptr<PeerListSearchController> searchController
@ -113,9 +111,7 @@ private:
};
class ContactsBoxController
: public PeerListController
, protected base::Subscriber {
class ContactsBoxController : public PeerListController {
public:
ContactsBoxController(
std::unique_ptr<PeerListSearchController> searchController

View file

@ -103,9 +103,12 @@ QString FormatVersionPrecise(int version) {
Changelogs::Changelogs(not_null<AuthSession*> session, int oldVersion)
: _session(session)
, _oldVersion(oldVersion) {
_chatsSubscription = subscribe(
_session->data().moreChatsLoaded(),
[=] { requestCloudLogs(); });
_session->data().chatsListChanges(
) | rpl::filter([](Data::Folder *folder) {
return !folder;
}) | rpl::start_with_next([=] {
requestCloudLogs();
}, _chatsSubscription);
}
std::unique_ptr<Changelogs> Changelogs::Create(
@ -117,7 +120,7 @@ std::unique_ptr<Changelogs> Changelogs::Create(
}
void Changelogs::requestCloudLogs() {
unsubscribe(base::take(_chatsSubscription));
_chatsSubscription.destroy();
const auto callback = [this](const MTPUpdates &result) {
_session->api().applyUpdates(result);

View file

@ -29,7 +29,7 @@ private:
const not_null<AuthSession*> _session;
const int _oldVersion = 0;
int _chatsSubscription = 0;
rpl::lifetime _chatsSubscription;
bool _addedSomeLocal = false;
};

View file

@ -774,6 +774,28 @@ bool Session::sendActionsAnimationCallback(crl::time now) {
return !_sendActions.empty();
}
bool Session::chatsListLoaded(Data::Folder *folder) {
return folder ? folder->chatsListLoaded() : _chatsListLoaded;
}
void Session::chatsListChanged(FolderId folderId) {
chatsListChanged(folderId ? folder(folderId).get() : nullptr);
}
void Session::chatsListChanged(Data::Folder *folder) {
_chatsListChanged.fire_copy(folder);
}
void Session::chatsListDone(FolderId folderId) {
const auto folder = folderId ? this->folder(folderId).get() : nullptr;
if (folder) {
folder->setChatsListLoaded(true);
} else {
_chatsListLoaded = true;
}
_chatsListLoadedEvents.fire_copy(folder);
}
Storage::Cache::Database &Session::cache() {
return *_cache;
}

View file

@ -135,15 +135,19 @@ public:
const MTPSendMessageAction &action,
TimeId when);
[[nodiscard]] base::Variable<bool> &contactsLoaded() {
[[nodiscard]] rpl::variable<bool> &contactsLoaded() {
return _contactsLoaded;
}
[[nodiscard]] base::Variable<bool> &allChatsLoaded() {
return _allChatsLoaded;
[[nodiscard]] rpl::producer<Data::Folder*> chatsListChanges() const {
return _chatsListChanged.events();
}
[[nodiscard]] base::Observable<void> &moreChatsLoaded() {
return _moreChatsLoaded;
[[nodiscard]] bool chatsListLoaded(Data::Folder *folder = nullptr);
[[nodiscard]] rpl::producer<Data::Folder*> chatsListLoadedEvents() const {
return _chatsListLoadedEvents.events();
}
void chatsListChanged(FolderId folderId);
void chatsListChanged(Data::Folder *folder);
void chatsListDone(FolderId folderId);
struct ItemVisibilityQuery {
not_null<HistoryItem*> item;
@ -725,9 +729,10 @@ private:
TimeId _exportAvailableAt = 0;
QPointer<BoxContent> _exportSuggestion;
base::Variable<bool> _contactsLoaded = { false };
base::Variable<bool> _allChatsLoaded = { false };
base::Observable<void> _moreChatsLoaded;
rpl::variable<bool> _contactsLoaded = false;
bool _chatsListLoaded = false;
rpl::event_stream<Data::Folder*> _chatsListLoadedEvents;
rpl::event_stream<Data::Folder*> _chatsListChanged;
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
rpl::event_stream<IdChange> _itemIdChanges;
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;

View file

@ -89,8 +89,12 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
_cancelSearchFromUser->setClickedCallback([this] { searchFromUserChanged.notify(nullptr); });
_cancelSearchFromUser->hide();
subscribe(session().downloaderTaskFinished(), [this] { update(); });
subscribe(session().data().contactsLoaded(), [this](bool) { refresh(); });
subscribe(session().downloaderTaskFinished(), [=] { update(); });
session().data().contactsLoaded().changes(
) | rpl::start_with_next([=] {
refresh();
}, lifetime());
session().data().itemRemoved(
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
@ -362,7 +366,7 @@ void DialogsInner::paintEvent(QPaintEvent *e) {
p.fillRect(dialogsClip, st::dialogsBg);
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
p.drawText(QRect(0, 0, fullWidth, st::noContactsHeight - (session().data().contactsLoaded().value() ? st::noContactsFont->height : 0)), lang(session().data().contactsLoaded().value() ? lng_no_chats : lng_contacts_loading), style::al_center);
p.drawText(QRect(0, 0, fullWidth, st::noContactsHeight - (session().data().contactsLoaded().current() ? st::noContactsFont->height : 0)), lang(session().data().contactsLoaded().current() ? lng_no_chats : lng_contacts_loading), style::al_center);
}
} else if (_state == State::Filtered) {
if (!_hashtagResults.empty()) {
@ -1953,12 +1957,16 @@ void DialogsInner::notify_historyMuteUpdated(History *history) {
refreshDialog(history);
}
Data::Folder *DialogsInner::shownFolder() const {
return _openedFolder;
}
void DialogsInner::refresh(bool toTop) {
int32 h = 0;
if (_state == State::Default) {
if (shownDialogs()->empty()) {
h = st::noContactsHeight;
if (session().data().contactsLoaded().value()) {
if (session().data().contactsLoaded().current()) {
if (_addContactLnk->isHidden()) _addContactLnk->show();
} else {
if (!_addContactLnk->isHidden()) _addContactLnk->hide();

View file

@ -68,6 +68,7 @@ public:
void scrollToEntry(const Dialogs::RowDescriptor &entry);
Data::Folder *shownFolder() const;
int32 lastSearchDate() const;
PeerData *lastSearchPeer() const;
MsgId lastSearchId() const;

View file

@ -183,10 +183,13 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
});
connect(_scroll, SIGNAL(geometryChanged()), _inner, SLOT(onParentGeometryChanged()));
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
subscribe(session().data().moreChatsLoaded(), [=] {
session().data().chatsListChanges(
) | rpl::filter([=](Data::Folder *folder) {
return (folder == _inner->shownFolder());
}) | rpl::start_with_next([=] {
_inner->refresh();
onListScroll();
});
}, lifetime());
connect(_filter, &Ui::FlatInput::cancelled, [=] {
onCancel();
});

View file

@ -1829,12 +1829,12 @@ void History::setFolderPointer(Data::Folder *folder) {
}
using Mode = Dialogs::Mode;
const auto wasAll = inChatList(Mode::All);
const auto wasImportant = inChatList(Mode::Important);
const auto wasImportant = wasAll && inChatList(Mode::Important);
if (wasAll) {
removeFromChatList(Mode::All);
}
if (wasImportant) {
removeFromChatList(Mode::Important);
if (wasImportant) {
removeFromChatList(Mode::Important);
}
}
const auto was = std::exchange(_folder, folder);
if (was) {
@ -1842,9 +1842,11 @@ void History::setFolderPointer(Data::Folder *folder) {
}
if (wasAll) {
addToChatList(Mode::All);
}
if (wasImportant) {
addToChatList(Mode::Important);
if (wasImportant) {
addToChatList(Mode::Important);
}
owner().chatsListChanged(was);
owner().chatsListChanged(_folder);
}
if (_folder) {
_folder->registerOne(this);

View file

@ -361,48 +361,55 @@ HistoryWidget::HistoryWidget(
confirmSendingFiles(data, CompressConfirm::No);
ActivateWindow(this->controller());
});
_attachDragPhoto->setDroppedCallback([this](const QMimeData *data) {
_attachDragPhoto->setDroppedCallback([=](const QMimeData *data) {
confirmSendingFiles(data, CompressConfirm::Yes);
ActivateWindow(this->controller());
});
subscribe(Adaptive::Changed(), [this] { update(); });
subscribe(Adaptive::Changed(), [=] { update(); });
Auth().data().itemRemoved(
) | rpl::start_with_next(
[this](auto item) { itemRemoved(item); },
lifetime());
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
itemRemoved(item);
}, lifetime());
Auth().data().historyChanged(
) | rpl::start_with_next(
[=](auto history) { handleHistoryChange(history); },
lifetime());
) | rpl::start_with_next([=](not_null<History*> history) {
handleHistoryChange(history);
}, lifetime());
Auth().data().viewResizeRequest(
) | rpl::start_with_next([this](auto view) {
) | rpl::start_with_next([=](not_null<HistoryView::Element*> view) {
if (view->data()->mainView() == view) {
updateHistoryGeometry();
}
}, lifetime());
Auth().data().itemViewRefreshRequest(
) | rpl::start_with_next([this](auto item) {
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
// While HistoryInner doesn't own item views we must refresh them
// even if the list is not yet created / was destroyed.
if (!_list) {
item->refreshMainView();
}
}, lifetime());
Auth().data().animationPlayInlineRequest(
) | rpl::start_with_next([=](auto item) {
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
if (const auto view = item->mainView()) {
if (const auto media = view->media()) {
media->playAnimation();
}
}
}, lifetime());
subscribe(Auth().data().contactsLoaded(), [this](bool) {
Auth().data().contactsLoaded().changes(
) | rpl::start_with_next([=] {
if (_peer) {
updateReportSpamStatus();
updateControlsVisibility();
}
});
}, lifetime());
subscribe(Media::Player::instance()->switchToNextNotifier(), [this](const Media::Player::Instance::Switch &pair) {
if (pair.from.type() == AudioMsgId::Type::Voice) {
scrollToCurrentVoiceMessage(pair.from.contextId(), pair.to);
@ -1863,7 +1870,7 @@ void HistoryWidget::updateReportSpamStatus() {
}
}
auto status = dbiprsRequesting;
if (!Auth().data().contactsLoaded().value() || _firstLoadRequest) {
if (!Auth().data().contactsLoaded().current() || _firstLoadRequest) {
status = dbiprsUnknown;
} else if (_peer->isUser()
&& _peer->asUser()->contactStatus() == UserData::ContactStatus::Contact) {

View file

@ -39,7 +39,9 @@ namespace {
constexpr auto kBlockedPerPage = 40;
class BlockUserBoxController : public ChatsListBoxController {
class BlockUserBoxController
: public ChatsListBoxController
, private base::Subscriber {
public:
void rowClicked(not_null<PeerListRow*> row) override;

View file

@ -620,6 +620,9 @@ void MainWindow::setInactivePress(bool inactive) {
}
}
MainWindow::~MainWindow() = default;
MainWindow::~MainWindow() {
// We want to delete all widgets before the _controller.
_body.destroy();
}
} // namespace Window

View file

@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
#include "observer_peer.h"
#include "styles/style_boxes.h"
#include "history/history.h"
#include "window/window_controller.h"
#include "support/support_helper.h"
@ -37,6 +36,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "dialogs/dialogs_key.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "styles/style_boxes.h"
#include "styles/style_window.h" // st::windowMinWidth
namespace Window {
namespace {
@ -56,6 +57,7 @@ private:
void addInfo();
void addSearch();
void addToggleUnreadMark();
void addToggleArchive();
void addUserActions(not_null<UserData*> user);
void addBlockUser(not_null<UserData*> user);
void addChatActions(not_null<ChatData*> chat);
@ -94,7 +96,7 @@ private:
History *FindWastedPin() {
const auto &order = Auth().data().pinnedDialogsOrder();
for (const auto pinned : order) {
for (const auto &pinned : order) {
if (const auto history = pinned.history()) {
if (history->peer->isChat()
&& history->peer->asChat()->isDeactivated()
@ -271,6 +273,22 @@ void Filler::addToggleUnreadMark() {
}, *lifetime);
}
void Filler::addToggleArchive() {
const auto peer = _peer;
const auto archived = [&] {
const auto history = peer->owner().historyLoaded(peer);
return history && history->folder();
}();
const auto toggle = [=] {
ToggleHistoryArchived(
peer->owner().history(peer),
!archived);
};
_addAction(
lang(archived ? lng_archived_remove : lng_archived_add),
toggle);
}
void Filler::addBlockUser(not_null<UserData*> user) {
auto blockText = [](not_null<UserData*> user) {
return lang(user->isBlocked()
@ -477,6 +495,9 @@ void Filler::fill() {
} else if (const auto channel = _peer->asChannel()) {
addChannelActions(channel);
}
if (_source == PeerMenuSource::ChatsList) {
addToggleArchive();
}
}
FolderFiller::FolderFiller(
@ -767,24 +788,17 @@ void PeerMenuAddMuteAction(
// [=] { Ui::hideLayer(); Auth().api().ungroupAllFromFeed(feed); }));
//}
//
//void ToggleChannelGrouping(not_null<ChannelData*> channel, bool group) {
// const auto callback = [=] {
// Ui::Toast::Show(lang(group
// ? lng_feed_channel_added
// : lng_feed_channel_removed));
// };
// if (group) {
// const auto feed = Auth().data().feed(Data::Feed::kId);
// if (feed->channels().size() < 2) {
// Info::FeedProfile::EditController::Start(feed, channel);
// return;
// }
// }
// Auth().api().toggleChannelGrouping(
// channel,
// group,
// callback);
//}
void ToggleHistoryArchived(not_null<History*> history, bool archived) {
const auto callback = [=] {
Ui::Toast::Show(lang(archived
? lng_chat_archived
: lng_chat_unarchived));
};
history->session().api().toggleHistoryArchived(
history,
archived,
callback);
}
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer) {
return [=] {

View file

@ -52,7 +52,7 @@ void PeerMenuAddChannelMembers(not_null<ChannelData*> channel);
//void PeerMenuUngroupFeed(not_null<Data::Feed*> feed); // #feed
void PeerMenuCreatePoll(not_null<PeerData*> peer);
//void ToggleChannelGrouping(not_null<ChannelData*> channel, bool group); // #feed
void ToggleHistoryArchived(not_null<History*> history, bool archived);
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer);
Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer);