| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759 |
- /*
- 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 "calls/calls_box_controller.h"
- #include "lang/lang_keys.h"
- #include "ui/effects/ripple_animation.h"
- #include "ui/widgets/labels.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/popup_menu.h"
- #include "ui/painter.h"
- #include "core/application.h"
- #include "calls/calls_instance.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "history/history_item_helpers.h"
- #include "mainwidget.h"
- #include "window/window_session_controller.h"
- #include "main/main_session.h"
- #include "data/data_session.h"
- #include "data/data_changes.h"
- #include "data/data_media_types.h"
- #include "data/data_user.h"
- #include "data/data_peer_values.h" // Data::ChannelHasActiveCall.
- #include "data/data_group_call.h"
- #include "data/data_channel.h"
- #include "boxes/delete_messages_box.h"
- #include "base/unixtime.h"
- #include "api/api_updates.h"
- #include "apiwrap.h"
- #include "styles/style_layers.h" // st::boxLabel.
- #include "styles/style_calls.h"
- #include "styles/style_boxes.h"
- #include "styles/style_menu_icons.h"
- namespace Calls {
- namespace {
- constexpr auto kFirstPageCount = 20;
- constexpr auto kPerPageCount = 100;
- class GroupCallRow final : public PeerListRow {
- public:
- GroupCallRow(not_null<PeerData*> peer);
- void rightActionAddRipple(
- QPoint point,
- Fn<void()> updateCallback) override;
- void rightActionStopLastRipple() override;
- int paintNameIconGetWidth(
- Painter &p,
- Fn<void()> repaint,
- crl::time now,
- int nameLeft,
- int nameTop,
- int nameWidth,
- int availableWidth,
- int outerWidth,
- bool selected) override {
- return 0;
- }
- QSize rightActionSize() const override {
- return peer()->isChannel() ? QSize(_st.width, _st.height) : QSize();
- }
- QMargins rightActionMargins() const override {
- return QMargins(
- 0,
- 0,
- st::defaultPeerListItem.photoPosition.x(),
- 0);
- }
- void rightActionPaint(
- Painter &p,
- int x,
- int y,
- int outerWidth,
- bool selected,
- bool actionSelected) override;
- private:
- const style::IconButton &_st;
- std::unique_ptr<Ui::RippleAnimation> _actionRipple;
- };
- GroupCallRow::GroupCallRow(not_null<PeerData*> peer)
- : PeerListRow(peer)
- , _st(st::callGroupCall) {
- if (const auto channel = peer->asChannel()) {
- const auto status = (channel->isMegagroup()
- ? (channel->isPublic()
- ? tr::lng_create_public_channel_title
- : tr::lng_create_private_channel_title)
- : (channel->isPublic()
- ? tr::lng_create_public_group_title
- : tr::lng_create_private_group_title))(tr::now);
- setCustomStatus(status.toLower());
- }
- }
- void GroupCallRow::rightActionPaint(
- Painter &p,
- int x,
- int y,
- int outerWidth,
- bool selected,
- bool actionSelected) {
- auto size = rightActionSize();
- if (_actionRipple) {
- _actionRipple->paint(
- p,
- x + _st.rippleAreaPosition.x(),
- y + _st.rippleAreaPosition.y(),
- outerWidth);
- if (_actionRipple->empty()) {
- _actionRipple.reset();
- }
- }
- _st.icon.paintInCenter(
- p,
- style::rtlrect(x, y, size.width(), size.height(), outerWidth));
- }
- void GroupCallRow::rightActionAddRipple(
- QPoint point,
- Fn<void()> updateCallback) {
- if (!_actionRipple) {
- auto mask = Ui::RippleAnimation::EllipseMask(
- QSize(_st.rippleAreaSize, _st.rippleAreaSize));
- _actionRipple = std::make_unique<Ui::RippleAnimation>(
- _st.ripple,
- std::move(mask),
- std::move(updateCallback));
- }
- _actionRipple->add(point - _st.rippleAreaPosition);
- }
- void GroupCallRow::rightActionStopLastRipple() {
- if (_actionRipple) {
- _actionRipple->lastStop();
- }
- }
- } // namespace
- namespace GroupCalls {
- ListController::ListController(not_null<Window::SessionController*> window)
- : _window(window) {
- setStyleOverrides(&st::peerListSingleRow);
- }
- Main::Session &ListController::session() const {
- return _window->session();
- }
- void ListController::prepare() {
- const auto removeRow = [=](not_null<PeerData*> peer) {
- const auto it = _groupCalls.find(peer->id);
- if (it != end(_groupCalls)) {
- const auto &row = it->second;
- delegate()->peerListRemoveRow(row);
- _groupCalls.erase(it);
- }
- };
- const auto createRow = [=](not_null<PeerData*> peer) {
- const auto it = _groupCalls.find(peer->id);
- if (it == end(_groupCalls)) {
- auto row = std::make_unique<GroupCallRow>(peer);
- _groupCalls.emplace(peer->id, row.get());
- delegate()->peerListAppendRow(std::move(row));
- }
- };
- const auto processPeer = [=](PeerData *peer) {
- if (!peer) {
- return;
- }
- const auto channel = peer->asChannel();
- if (channel && Data::ChannelHasActiveCall(channel)) {
- createRow(peer);
- } else {
- removeRow(peer);
- }
- };
- const auto finishProcess = [=] {
- delegate()->peerListRefreshRows();
- _fullCount = delegate()->peerListFullRowsCount();
- };
- session().changes().peerUpdates(
- Data::PeerUpdate::Flag::GroupCall
- ) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
- processPeer(update.peer);
- finishProcess();
- }, lifetime());
- {
- auto count = 0;
- const auto list = session().data().chatsList(nullptr);
- for (const auto &key : list->pinned()->order()) {
- processPeer(key.peer());
- }
- for (const auto &key : list->indexed()->all()) {
- if (count > kFirstPageCount) {
- break;
- }
- processPeer(key->key().peer());
- count++;
- }
- finishProcess();
- }
- }
- rpl::producer<bool> ListController::shownValue() const {
- return _fullCount.value(
- ) | rpl::map(rpl::mappers::_1 > 0) | rpl::distinct_until_changed();
- }
- void ListController::rowClicked(not_null<PeerListRow*> row) {
- const auto window = _window;
- crl::on_main(window, [=, peer = row->peer()] {
- window->showPeerHistory(
- peer,
- Window::SectionShow::Way::ClearStack);
- });
- }
- void ListController::rowRightActionClicked(not_null<PeerListRow*> row) {
- _window->startOrJoinGroupCall(row->peer());
- }
- } // namespace GroupCalls
- class BoxController::Row : public PeerListRow {
- public:
- Row(not_null<HistoryItem*> item);
- enum class Type {
- Out,
- In,
- Missed,
- };
- enum class CallType {
- Voice,
- Video,
- };
- bool canAddItem(not_null<const HistoryItem*> item) const {
- return (ComputeType(item) == _type)
- && (!hasItems() || _items.front()->history() == item->history())
- && (ItemDateTime(item).date() == _date);
- }
- void addItem(not_null<HistoryItem*> item) {
- Expects(canAddItem(item));
- _items.push_back(item);
- ranges::sort(_items, [](not_null<HistoryItem*> a, auto b) {
- return (a->id > b->id);
- });
- refreshStatus();
- }
- void itemRemoved(not_null<const HistoryItem*> item) {
- if (hasItems() && item->id >= minItemId() && item->id <= maxItemId()) {
- _items.erase(std::remove(_items.begin(), _items.end(), item), _items.end());
- refreshStatus();
- }
- }
- [[nodiscard]] bool hasItems() const {
- return !_items.empty();
- }
- [[nodiscard]] MsgId minItemId() const {
- Expects(hasItems());
- return _items.back()->id;
- }
- [[nodiscard]] MsgId maxItemId() const {
- Expects(hasItems());
- return _items.front()->id;
- }
- [[nodiscard]] const std::vector<not_null<HistoryItem*>> &items() const {
- return _items;
- }
- void paintStatusText(
- Painter &p,
- const style::PeerListItem &st,
- int x,
- int y,
- int availableWidth,
- int outerWidth,
- bool selected) override;
- void rightActionAddRipple(
- QPoint point,
- Fn<void()> updateCallback) override;
- void rightActionStopLastRipple() override;
- int paintNameIconGetWidth(
- Painter &p,
- Fn<void()> repaint,
- crl::time now,
- int nameLeft,
- int nameTop,
- int nameWidth,
- int availableWidth,
- int outerWidth,
- bool selected) override {
- return 0;
- }
- QSize rightActionSize() const override {
- return peer()->isUser() ? QSize(_st->width, _st->height) : QSize();
- }
- QMargins rightActionMargins() const override {
- return QMargins(
- 0,
- 0,
- st::defaultPeerListItem.photoPosition.x(),
- 0);
- }
- void rightActionPaint(
- Painter &p,
- int x,
- int y,
- int outerWidth,
- bool selected,
- bool actionSelected) override;
- private:
- void refreshStatus() override;
- static Type ComputeType(not_null<const HistoryItem*> item);
- static CallType ComputeCallType(not_null<const HistoryItem*> item);
- std::vector<not_null<HistoryItem*>> _items;
- QDate _date;
- Type _type;
- not_null<const style::IconButton*> _st;
- std::unique_ptr<Ui::RippleAnimation> _actionRipple;
- };
- BoxController::Row::Row(not_null<HistoryItem*> item)
- : PeerListRow(item->history()->peer, item->id.bare)
- , _items(1, item)
- , _date(ItemDateTime(item).date())
- , _type(ComputeType(item))
- , _st(ComputeCallType(item) == CallType::Voice
- ? &st::callReDial
- : &st::callCameraReDial) {
- refreshStatus();
- }
- void BoxController::Row::paintStatusText(Painter &p, const style::PeerListItem &st, int x, int y, int availableWidth, int outerWidth, bool selected) {
- auto icon = ([this] {
- switch (_type) {
- case Type::In: return &st::callArrowIn;
- case Type::Out: return &st::callArrowOut;
- case Type::Missed: return &st::callArrowMissed;
- }
- Unexpected("_type in Calls::BoxController::Row::paintStatusText().");
- })();
- icon->paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth);
- auto shift = st::callArrowPosition.x() + icon->width() + st::callArrowSkip;
- x += shift;
- availableWidth -= shift;
- PeerListRow::paintStatusText(p, st, x, y, availableWidth, outerWidth, selected);
- }
- void BoxController::Row::rightActionPaint(
- Painter &p,
- int x,
- int y,
- int outerWidth,
- bool selected,
- bool actionSelected) {
- auto size = rightActionSize();
- if (_actionRipple) {
- _actionRipple->paint(
- p,
- x + _st->rippleAreaPosition.x(),
- y + _st->rippleAreaPosition.y(),
- outerWidth);
- if (_actionRipple->empty()) {
- _actionRipple.reset();
- }
- }
- _st->icon.paintInCenter(
- p,
- style::rtlrect(x, y, size.width(), size.height(), outerWidth));
- }
- void BoxController::Row::refreshStatus() {
- if (!hasItems()) {
- return;
- }
- auto text = [this] {
- auto time = QLocale().toString(ItemDateTime(_items.front()).time(), QLocale::ShortFormat);
- auto today = QDateTime::currentDateTime().date();
- if (_date == today) {
- return tr::lng_call_box_status_today(tr::now, lt_time, time);
- } else if (_date.addDays(1) == today) {
- return tr::lng_call_box_status_yesterday(tr::now, lt_time, time);
- }
- return tr::lng_call_box_status_date(tr::now, lt_date, langDayOfMonthFull(_date), lt_time, time);
- };
- setCustomStatus((_items.size() > 1)
- ? tr::lng_call_box_status_group(
- tr::now,
- lt_amount,
- QString::number(_items.size()),
- lt_status,
- text())
- : text());
- }
- BoxController::Row::Type BoxController::Row::ComputeType(
- not_null<const HistoryItem*> item) {
- if (item->out()) {
- return Type::Out;
- } else if (auto media = item->media()) {
- if (const auto call = media->call()) {
- const auto reason = call->finishReason;
- if (reason == Data::Call::FinishReason::Busy
- || reason == Data::Call::FinishReason::Missed) {
- return Type::Missed;
- }
- }
- }
- return Type::In;
- }
- BoxController::Row::CallType BoxController::Row::ComputeCallType(
- not_null<const HistoryItem*> item) {
- if (auto media = item->media()) {
- if (const auto call = media->call()) {
- if (call->video) {
- return CallType::Video;
- }
- }
- }
- return CallType::Voice;
- }
- void BoxController::Row::rightActionAddRipple(QPoint point, Fn<void()> updateCallback) {
- if (!_actionRipple) {
- auto mask = Ui::RippleAnimation::EllipseMask(
- QSize(_st->rippleAreaSize, _st->rippleAreaSize));
- _actionRipple = std::make_unique<Ui::RippleAnimation>(
- _st->ripple,
- std::move(mask),
- std::move(updateCallback));
- }
- _actionRipple->add(point - _st->rippleAreaPosition);
- }
- void BoxController::Row::rightActionStopLastRipple() {
- if (_actionRipple) {
- _actionRipple->lastStop();
- }
- }
- BoxController::BoxController(not_null<Window::SessionController*> window)
- : _window(window)
- , _api(&_window->session().mtp()) {
- }
- Main::Session &BoxController::session() const {
- return _window->session();
- }
- void BoxController::prepare() {
- session().data().itemRemoved(
- ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
- if (const auto row = rowForItem(item)) {
- row->itemRemoved(item);
- if (!row->hasItems()) {
- delegate()->peerListRemoveRow(row);
- if (!delegate()->peerListFullRowsCount()) {
- refreshAbout();
- }
- }
- delegate()->peerListRefreshRows();
- }
- }, lifetime());
- session().changes().messageUpdates(
- Data::MessageUpdate::Flag::NewAdded
- ) | rpl::filter([=](const Data::MessageUpdate &update) {
- const auto media = update.item->media();
- return (media != nullptr) && (media->call() != nullptr);
- }) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
- insertRow(update.item, InsertWay::Prepend);
- }, lifetime());
- delegate()->peerListSetTitle(tr::lng_call_box_title());
- setDescriptionText(tr::lng_contacts_loading(tr::now));
- delegate()->peerListRefreshRows();
- loadMoreRows();
- }
- void BoxController::loadMoreRows() {
- if (_loadRequestId || _allLoaded) {
- return;
- }
- _loadRequestId = _api.request(MTPmessages_Search(
- MTP_flags(0),
- MTP_inputPeerEmpty(),
- MTP_string(), // q
- MTP_inputPeerEmpty(),
- MTPInputPeer(), // saved_peer_id
- MTPVector<MTPReaction>(), // saved_reaction
- MTPint(), // top_msg_id
- MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)),
- MTP_int(0), // min_date
- MTP_int(0), // max_date
- MTP_int(_offsetId),
- MTP_int(0), // add_offset
- MTP_int(_offsetId ? kFirstPageCount : kPerPageCount),
- MTP_int(0), // max_id
- MTP_int(0), // min_id
- MTP_long(0) // hash
- )).done([this](const MTPmessages_Messages &result) {
- _loadRequestId = 0;
- auto handleResult = [&](auto &data) {
- session().data().processUsers(data.vusers());
- session().data().processChats(data.vchats());
- receivedCalls(data.vmessages().v);
- };
- switch (result.type()) {
- case mtpc_messages_messages: handleResult(result.c_messages_messages()); _allLoaded = true; break;
- case mtpc_messages_messagesSlice: handleResult(result.c_messages_messagesSlice()); break;
- case mtpc_messages_channelMessages: {
- LOG(("API Error: received messages.channelMessages! (Calls::BoxController::preloadRows)"));
- handleResult(result.c_messages_channelMessages());
- } break;
- case mtpc_messages_messagesNotModified: {
- LOG(("API Error: received messages.messagesNotModified! (Calls::BoxController::preloadRows)"));
- } break;
- default: Unexpected("Type of messages.Messages (Calls::BoxController::preloadRows)");
- }
- }).fail([this] {
- _loadRequestId = 0;
- }).send();
- }
- base::unique_qptr<Ui::PopupMenu> BoxController::rowContextMenu(
- QWidget *parent,
- not_null<PeerListRow*> row) {
- const auto &items = static_cast<Row*>(row.get())->items();
- const auto session = &this->session();
- const auto ids = session->data().itemsToIds(items);
- auto result = base::make_unique_q<Ui::PopupMenu>(
- parent,
- st::popupMenuWithIcons);
- result->addAction(tr::lng_context_delete_selected(tr::now), [=] {
- _window->show(
- Box<DeleteMessagesBox>(session, base::duplicate(ids)));
- }, &st::menuIconDelete);
- result->addAction(tr::lng_context_to_msg(tr::now), [=, window = _window] {
- if (const auto item = session->data().message(ids.front())) {
- window->showMessage(item);
- }
- }, &st::menuIconShowInChat);
- return result;
- }
- void BoxController::refreshAbout() {
- setDescriptionText(delegate()->peerListFullRowsCount() ? QString() : tr::lng_call_box_about(tr::now));
- }
- void BoxController::rowClicked(not_null<PeerListRow*> row) {
- const auto itemsRow = static_cast<Row*>(row.get());
- const auto itemId = itemsRow->maxItemId();
- const auto window = _window;
- crl::on_main(window, [=, peer = row->peer()] {
- window->showPeerHistory(
- peer,
- Window::SectionShow::Way::ClearStack,
- itemId);
- });
- }
- void BoxController::rowRightActionClicked(not_null<PeerListRow*> row) {
- auto user = row->peer()->asUser();
- Assert(user != nullptr);
- Core::App().calls().startOutgoingCall(user, false);
- }
- void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
- if (result.empty()) {
- _allLoaded = true;
- }
- for (const auto &message : result) {
- const auto msgId = IdFromMessage(message);
- const auto peerId = PeerFromMessage(message);
- if (const auto peer = session().data().peerLoaded(peerId)) {
- const auto item = session().data().addNewMessage(
- message,
- MessageFlags(),
- NewMessageType::Existing);
- insertRow(item, InsertWay::Append);
- } else {
- LOG(("API Error: a search results with not loaded peer %1"
- ).arg(peerId.value));
- }
- _offsetId = msgId;
- }
- refreshAbout();
- delegate()->peerListRefreshRows();
- }
- bool BoxController::insertRow(
- not_null<HistoryItem*> item,
- InsertWay way) {
- if (auto row = rowForItem(item)) {
- if (row->canAddItem(item)) {
- row->addItem(item);
- return false;
- }
- }
- (way == InsertWay::Append)
- ? delegate()->peerListAppendRow(createRow(item))
- : delegate()->peerListPrependRow(createRow(item));
- delegate()->peerListSortRows([](
- const PeerListRow &a,
- const PeerListRow &b) {
- return static_cast<const Row&>(a).maxItemId()
- > static_cast<const Row&>(b).maxItemId();
- });
- return true;
- }
- BoxController::Row *BoxController::rowForItem(not_null<const HistoryItem*> item) {
- auto v = delegate();
- if (auto fullRowsCount = v->peerListFullRowsCount()) {
- auto itemId = item->id;
- auto lastRow = static_cast<Row*>(v->peerListRowAt(fullRowsCount - 1).get());
- if (itemId < lastRow->minItemId()) {
- return lastRow;
- }
- auto firstRow = static_cast<Row*>(v->peerListRowAt(0).get());
- if (itemId > firstRow->maxItemId()) {
- return firstRow;
- }
- // Binary search. Invariant:
- // 1. rowAt(left)->maxItemId() >= itemId.
- // 2. (left + 1 == fullRowsCount) OR rowAt(left + 1)->maxItemId() < itemId.
- auto left = 0;
- auto right = fullRowsCount;
- while (left + 1 < right) {
- auto middle = (right + left) / 2;
- auto middleRow = static_cast<Row*>(v->peerListRowAt(middle).get());
- if (middleRow->maxItemId() >= itemId) {
- left = middle;
- } else {
- right = middle;
- }
- }
- auto result = static_cast<Row*>(v->peerListRowAt(left).get());
- // Check for rowAt(left)->minItemId > itemId > rowAt(left + 1)->maxItemId.
- // In that case we sometimes need to return rowAt(left + 1), not rowAt(left).
- if (result->minItemId() > itemId && left + 1 < fullRowsCount) {
- auto possibleResult = static_cast<Row*>(v->peerListRowAt(left + 1).get());
- Assert(possibleResult->maxItemId() < itemId);
- if (possibleResult->canAddItem(item)) {
- return possibleResult;
- }
- }
- return result;
- }
- return nullptr;
- }
- std::unique_ptr<PeerListRow> BoxController::createRow(
- not_null<HistoryItem*> item) const {
- return std::make_unique<Row>(item);
- }
- void ClearCallsBox(
- not_null<Ui::GenericBox*> box,
- not_null<Window::SessionController*> window) {
- const auto weak = Ui::MakeWeak(box);
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box,
- tr::lng_call_box_clear_sure(),
- st::boxLabel),
- st::boxPadding);
- const auto revokeCheckbox = box->addRow(
- object_ptr<Ui::Checkbox>(
- box,
- tr::lng_delete_for_everyone_check(tr::now),
- false,
- st::defaultBoxCheckbox),
- style::margins(
- st::boxPadding.left(),
- st::boxPadding.bottom(),
- st::boxPadding.right(),
- st::boxPadding.bottom()));
- const auto api = &window->session().api();
- const auto sendRequest = [=](bool revoke, auto self) -> void {
- using Flag = MTPmessages_DeletePhoneCallHistory::Flag;
- api->request(MTPmessages_DeletePhoneCallHistory(
- MTP_flags(revoke ? Flag::f_revoke : Flag(0))
- )).done([=](const MTPmessages_AffectedFoundMessages &result) {
- result.match([&](
- const MTPDmessages_affectedFoundMessages &data) {
- api->applyUpdates(MTP_updates(
- MTP_vector<MTPUpdate>(
- 1,
- MTP_updateDeleteMessages(
- data.vmessages(),
- data.vpts(),
- data.vpts_count())),
- MTP_vector<MTPUser>(),
- MTP_vector<MTPChat>(),
- MTP_int(base::unixtime::now()),
- MTP_int(0)));
- const auto offset = data.voffset().v;
- if (offset > 0) {
- self(revoke, self);
- } else {
- api->session().data().destroyAllCallItems();
- if (const auto strong = weak.data()) {
- strong->closeBox();
- }
- }
- });
- }).send();
- };
- box->addButton(tr::lng_call_box_clear_button(), [=] {
- sendRequest(revokeCheckbox->checked(), sendRequest);
- });
- box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
- }
- } // namespace Calls
|