2017-04-27 19:04:45 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2017-04-27 19:04:45 +00:00
|
|
|
*/
|
|
|
|
#include "calls/calls_box_controller.h"
|
|
|
|
|
2017-04-13 08:27:10 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2017-04-27 19:04:45 +00:00
|
|
|
#include "ui/effects/ripple_animation.h"
|
2021-01-21 15:57:12 +00:00
|
|
|
#include "ui/widgets/labels.h"
|
|
|
|
#include "ui/widgets/checkbox.h"
|
|
|
|
#include "ui/widgets/popup_menu.h"
|
2022-09-16 20:23:27 +00:00
|
|
|
#include "ui/painter.h"
|
2020-06-25 17:57:36 +00:00
|
|
|
#include "core/application.h"
|
2017-04-27 19:04:45 +00:00
|
|
|
#include "calls/calls_instance.h"
|
2018-01-13 12:45:11 +00:00
|
|
|
#include "history/history.h"
|
|
|
|
#include "history/history_item.h"
|
2022-12-14 12:15:46 +00:00
|
|
|
#include "history/history_item_helpers.h"
|
2017-10-03 13:05:58 +00:00
|
|
|
#include "mainwidget.h"
|
2019-07-25 18:55:11 +00:00
|
|
|
#include "window/window_session_controller.h"
|
2019-07-24 11:45:24 +00:00
|
|
|
#include "main/main_session.h"
|
2018-01-04 10:22:53 +00:00
|
|
|
#include "data/data_session.h"
|
2020-06-25 17:57:36 +00:00
|
|
|
#include "data/data_changes.h"
|
2018-01-14 16:02:25 +00:00
|
|
|
#include "data/data_media_types.h"
|
2019-08-06 16:40:08 +00:00
|
|
|
#include "data/data_user.h"
|
2023-01-20 15:18:24 +00:00
|
|
|
#include "data/data_peer_values.h" // Data::ChannelHasActiveCall.
|
|
|
|
#include "data/data_group_call.h"
|
|
|
|
#include "data/data_channel.h"
|
2021-10-18 20:55:58 +00:00
|
|
|
#include "boxes/delete_messages_box.h"
|
2021-01-26 10:28:25 +00:00
|
|
|
#include "base/unixtime.h"
|
|
|
|
#include "api/api_updates.h"
|
2021-01-21 15:57:12 +00:00
|
|
|
#include "apiwrap.h"
|
|
|
|
#include "styles/style_layers.h" // st::boxLabel.
|
|
|
|
#include "styles/style_calls.h"
|
|
|
|
#include "styles/style_boxes.h"
|
2021-12-09 17:56:24 +00:00
|
|
|
#include "styles/style_menu_icons.h"
|
2017-04-27 19:04:45 +00:00
|
|
|
|
|
|
|
namespace Calls {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kFirstPageCount = 20;
|
|
|
|
constexpr auto kPerPageCount = 100;
|
|
|
|
|
2023-01-20 15:18:24 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
} // namespace
|
|
|
|
|
2023-01-20 15:18:24 +00:00
|
|
|
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
|
|
|
|
|
2017-06-13 21:08:47 +00:00
|
|
|
class BoxController::Row : public PeerListRow {
|
2017-04-27 19:04:45 +00:00
|
|
|
public:
|
2017-10-05 15:35:52 +00:00
|
|
|
Row(not_null<HistoryItem*> item);
|
2017-04-27 19:04:45 +00:00
|
|
|
|
|
|
|
enum class Type {
|
|
|
|
Out,
|
|
|
|
In,
|
|
|
|
Missed,
|
|
|
|
};
|
|
|
|
|
2020-08-15 12:30:21 +00:00
|
|
|
enum class CallType {
|
|
|
|
Voice,
|
|
|
|
Video,
|
|
|
|
};
|
|
|
|
|
2017-10-05 15:35:52 +00:00
|
|
|
bool canAddItem(not_null<const HistoryItem*> item) const {
|
2018-02-03 19:52:35 +00:00
|
|
|
return (ComputeType(item) == _type)
|
2021-01-21 15:57:12 +00:00
|
|
|
&& (!hasItems() || _items.front()->history() == item->history())
|
2018-02-03 19:52:35 +00:00
|
|
|
&& (ItemDateTime(item).date() == _date);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2017-11-20 12:23:20 +00:00
|
|
|
void addItem(not_null<HistoryItem*> item) {
|
2017-04-27 19:04:45 +00:00
|
|
|
Expects(canAddItem(item));
|
2018-06-03 13:30:40 +00:00
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
_items.push_back(item);
|
2017-11-20 12:23:20 +00:00
|
|
|
ranges::sort(_items, [](not_null<HistoryItem*> a, auto b) {
|
2017-04-27 19:04:45 +00:00
|
|
|
return (a->id > b->id);
|
|
|
|
});
|
|
|
|
refreshStatus();
|
|
|
|
}
|
2017-10-05 15:35:52 +00:00
|
|
|
void itemRemoved(not_null<const HistoryItem*> item) {
|
2017-04-27 19:04:45 +00:00
|
|
|
if (hasItems() && item->id >= minItemId() && item->id <= maxItemId()) {
|
|
|
|
_items.erase(std::remove(_items.begin(), _items.end(), item), _items.end());
|
|
|
|
refreshStatus();
|
|
|
|
}
|
|
|
|
}
|
2021-01-21 15:57:12 +00:00
|
|
|
[[nodiscard]] bool hasItems() const {
|
2017-04-27 19:04:45 +00:00
|
|
|
return !_items.empty();
|
|
|
|
}
|
|
|
|
|
2021-01-21 15:57:12 +00:00
|
|
|
[[nodiscard]] MsgId minItemId() const {
|
2017-04-27 19:04:45 +00:00
|
|
|
Expects(hasItems());
|
2021-01-21 15:57:12 +00:00
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
return _items.back()->id;
|
|
|
|
}
|
|
|
|
|
2021-01-21 15:57:12 +00:00
|
|
|
[[nodiscard]] MsgId maxItemId() const {
|
2017-04-27 19:04:45 +00:00
|
|
|
Expects(hasItems());
|
2021-01-21 15:57:12 +00:00
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
return _items.front()->id;
|
|
|
|
}
|
|
|
|
|
2021-01-21 15:57:12 +00:00
|
|
|
[[nodiscard]] const std::vector<not_null<HistoryItem*>> &items() const {
|
|
|
|
return _items;
|
|
|
|
}
|
|
|
|
|
2017-09-26 17:57:01 +00:00
|
|
|
void paintStatusText(
|
|
|
|
Painter &p,
|
|
|
|
const style::PeerListItem &st,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int availableWidth,
|
|
|
|
int outerWidth,
|
|
|
|
bool selected) override;
|
2021-10-13 12:37:38 +00:00
|
|
|
void rightActionAddRipple(
|
|
|
|
QPoint point,
|
|
|
|
Fn<void()> updateCallback) override;
|
|
|
|
void rightActionStopLastRipple() override;
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2022-08-11 17:22:21 +00:00
|
|
|
int paintNameIconGetWidth(
|
|
|
|
Painter &p,
|
|
|
|
Fn<void()> repaint,
|
|
|
|
crl::time now,
|
|
|
|
int nameLeft,
|
|
|
|
int nameTop,
|
|
|
|
int nameWidth,
|
|
|
|
int availableWidth,
|
|
|
|
int outerWidth,
|
|
|
|
bool selected) override {
|
2017-11-07 17:53:00 +00:00
|
|
|
return 0;
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2021-10-13 12:37:38 +00:00
|
|
|
QSize rightActionSize() const override {
|
2020-08-15 12:30:21 +00:00
|
|
|
return peer()->isUser() ? QSize(_st->width, _st->height) : QSize();
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2021-10-13 12:37:38 +00:00
|
|
|
QMargins rightActionMargins() const override {
|
2017-11-07 17:53:00 +00:00
|
|
|
return QMargins(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
st::defaultPeerListItem.photoPosition.x(),
|
|
|
|
0);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2021-10-13 12:37:38 +00:00
|
|
|
void rightActionPaint(
|
2017-11-07 17:53:00 +00:00
|
|
|
Painter &p,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
bool selected,
|
|
|
|
bool actionSelected) override;
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2017-06-13 21:08:47 +00:00
|
|
|
private:
|
2020-04-30 15:31:44 +00:00
|
|
|
void refreshStatus() override;
|
2017-10-05 15:35:52 +00:00
|
|
|
static Type ComputeType(not_null<const HistoryItem*> item);
|
2020-08-15 12:30:21 +00:00
|
|
|
static CallType ComputeCallType(not_null<const HistoryItem*> item);
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2017-11-20 12:23:20 +00:00
|
|
|
std::vector<not_null<HistoryItem*>> _items;
|
2017-04-27 19:04:45 +00:00
|
|
|
QDate _date;
|
|
|
|
Type _type;
|
2020-08-15 12:30:21 +00:00
|
|
|
not_null<const style::IconButton*> _st;
|
2017-04-27 19:04:45 +00:00
|
|
|
|
|
|
|
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-10-05 15:35:52 +00:00
|
|
|
BoxController::Row::Row(not_null<HistoryItem*> item)
|
2021-09-30 06:04:55 +00:00
|
|
|
: PeerListRow(item->history()->peer, item->id.bare)
|
2017-04-27 19:04:45 +00:00
|
|
|
, _items(1, item)
|
2018-02-03 19:52:35 +00:00
|
|
|
, _date(ItemDateTime(item).date())
|
2020-08-15 12:30:21 +00:00
|
|
|
, _type(ComputeType(item))
|
|
|
|
, _st(ComputeCallType(item) == CallType::Voice
|
|
|
|
? &st::callReDial
|
|
|
|
: &st::callCameraReDial) {
|
2017-04-27 19:04:45 +00:00
|
|
|
refreshStatus();
|
|
|
|
}
|
|
|
|
|
2017-09-26 17:57:01 +00:00
|
|
|
void BoxController::Row::paintStatusText(Painter &p, const style::PeerListItem &st, int x, int y, int availableWidth, int outerWidth, bool selected) {
|
2017-05-02 07:25:20 +00:00
|
|
|
auto icon = ([this] {
|
2017-04-27 19:04:45 +00:00
|
|
|
switch (_type) {
|
2017-05-02 07:25:20 +00:00
|
|
|
case Type::In: return &st::callArrowIn;
|
|
|
|
case Type::Out: return &st::callArrowOut;
|
|
|
|
case Type::Missed: return &st::callArrowMissed;
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
Unexpected("_type in Calls::BoxController::Row::paintStatusText().");
|
|
|
|
})();
|
2017-05-02 07:25:20 +00:00
|
|
|
icon->paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth);
|
2017-06-28 05:57:58 +00:00
|
|
|
auto shift = st::callArrowPosition.x() + icon->width() + st::callArrowSkip;
|
|
|
|
x += shift;
|
|
|
|
availableWidth -= shift;
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2017-09-26 17:57:01 +00:00
|
|
|
PeerListRow::paintStatusText(p, st, x, y, availableWidth, outerWidth, selected);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 12:37:38 +00:00
|
|
|
void BoxController::Row::rightActionPaint(
|
2017-11-07 17:53:00 +00:00
|
|
|
Painter &p,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
bool selected,
|
|
|
|
bool actionSelected) {
|
2021-10-13 12:37:38 +00:00
|
|
|
auto size = rightActionSize();
|
2017-04-27 19:04:45 +00:00
|
|
|
if (_actionRipple) {
|
2020-08-15 12:30:21 +00:00
|
|
|
_actionRipple->paint(
|
|
|
|
p,
|
|
|
|
x + _st->rippleAreaPosition.x(),
|
|
|
|
y + _st->rippleAreaPosition.y(),
|
|
|
|
outerWidth);
|
2017-04-27 19:04:45 +00:00
|
|
|
if (_actionRipple->empty()) {
|
|
|
|
_actionRipple.reset();
|
|
|
|
}
|
|
|
|
}
|
2020-08-15 12:30:21 +00:00
|
|
|
_st->icon.paintInCenter(
|
|
|
|
p,
|
|
|
|
style::rtlrect(x, y, size.width(), size.height(), outerWidth));
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BoxController::Row::refreshStatus() {
|
|
|
|
if (!hasItems()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto text = [this] {
|
2022-12-15 09:47:56 +00:00
|
|
|
auto time = QLocale().toString(ItemDateTime(_items.front()).time(), QLocale::ShortFormat);
|
2017-04-27 19:04:45 +00:00
|
|
|
auto today = QDateTime::currentDateTime().date();
|
|
|
|
if (_date == today) {
|
2019-06-19 16:39:25 +00:00
|
|
|
return tr::lng_call_box_status_today(tr::now, lt_time, time);
|
2017-04-27 19:04:45 +00:00
|
|
|
} else if (_date.addDays(1) == today) {
|
2019-06-19 16:39:25 +00:00
|
|
|
return tr::lng_call_box_status_yesterday(tr::now, lt_time, time);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2019-06-19 16:39:25 +00:00
|
|
|
return tr::lng_call_box_status_date(tr::now, lt_date, langDayOfMonthFull(_date), lt_time, time);
|
2017-04-27 19:04:45 +00:00
|
|
|
};
|
2019-06-19 12:41:00 +00:00
|
|
|
setCustomStatus((_items.size() > 1)
|
|
|
|
? tr::lng_call_box_status_group(
|
|
|
|
tr::now,
|
|
|
|
lt_amount,
|
|
|
|
QString::number(_items.size()),
|
|
|
|
lt_status,
|
|
|
|
text())
|
|
|
|
: text());
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-10-05 15:35:52 +00:00
|
|
|
BoxController::Row::Type BoxController::Row::ComputeType(
|
|
|
|
not_null<const HistoryItem*> item) {
|
2017-04-27 19:04:45 +00:00
|
|
|
if (item->out()) {
|
|
|
|
return Type::Out;
|
2018-01-14 16:02:25 +00:00
|
|
|
} 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) {
|
2017-04-27 21:17:00 +00:00
|
|
|
return Type::Missed;
|
|
|
|
}
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Type::In;
|
|
|
|
}
|
|
|
|
|
2020-08-15 12:30:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-10-13 12:37:38 +00:00
|
|
|
void BoxController::Row::rightActionAddRipple(QPoint point, Fn<void()> updateCallback) {
|
2017-04-27 19:04:45 +00:00
|
|
|
if (!_actionRipple) {
|
2022-10-03 11:11:05 +00:00
|
|
|
auto mask = Ui::RippleAnimation::EllipseMask(
|
2020-08-15 12:30:21 +00:00
|
|
|
QSize(_st->rippleAreaSize, _st->rippleAreaSize));
|
|
|
|
_actionRipple = std::make_unique<Ui::RippleAnimation>(
|
|
|
|
_st->ripple,
|
|
|
|
std::move(mask),
|
|
|
|
std::move(updateCallback));
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2020-08-15 12:30:21 +00:00
|
|
|
_actionRipple->add(point - _st->rippleAreaPosition);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 12:37:38 +00:00
|
|
|
void BoxController::Row::rightActionStopLastRipple() {
|
2017-04-27 19:04:45 +00:00
|
|
|
if (_actionRipple) {
|
|
|
|
_actionRipple->lastStop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-25 18:55:11 +00:00
|
|
|
BoxController::BoxController(not_null<Window::SessionController*> window)
|
2019-11-27 08:02:56 +00:00
|
|
|
: _window(window)
|
2020-06-17 09:36:25 +00:00
|
|
|
, _api(&_window->session().mtp()) {
|
2019-07-25 18:55:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Main::Session &BoxController::session() const {
|
|
|
|
return _window->session();
|
|
|
|
}
|
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
void BoxController::prepare() {
|
2019-07-25 18:55:11 +00:00
|
|
|
session().data().itemRemoved(
|
2019-04-25 12:45:15 +00:00
|
|
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
|
|
|
if (const auto row = rowForItem(item)) {
|
2017-12-22 07:05:20 +00:00
|
|
|
row->itemRemoved(item);
|
|
|
|
if (!row->hasItems()) {
|
|
|
|
delegate()->peerListRemoveRow(row);
|
|
|
|
if (!delegate()->peerListFullRowsCount()) {
|
|
|
|
refreshAbout();
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-22 07:05:20 +00:00
|
|
|
delegate()->peerListRefreshRows();
|
|
|
|
}
|
|
|
|
}, lifetime());
|
2019-08-06 16:40:08 +00:00
|
|
|
|
2020-06-25 17:57:36 +00:00
|
|
|
session().changes().messageUpdates(
|
2020-08-28 10:01:55 +00:00
|
|
|
Data::MessageUpdate::Flag::NewAdded
|
|
|
|
) | rpl::filter([=](const Data::MessageUpdate &update) {
|
2020-10-01 14:52:02 +00:00
|
|
|
const auto media = update.item->media();
|
|
|
|
return (media != nullptr) && (media->call() != nullptr);
|
2020-08-28 10:01:55 +00:00
|
|
|
}) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
2020-06-25 17:57:36 +00:00
|
|
|
insertRow(update.item, InsertWay::Prepend);
|
|
|
|
}, lifetime());
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2019-06-18 15:00:55 +00:00
|
|
|
delegate()->peerListSetTitle(tr::lng_call_box_title());
|
2019-06-19 15:09:03 +00:00
|
|
|
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
2017-06-13 21:08:47 +00:00
|
|
|
delegate()->peerListRefreshRows();
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2017-06-14 10:46:06 +00:00
|
|
|
loadMoreRows();
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-06-14 10:46:06 +00:00
|
|
|
void BoxController::loadMoreRows() {
|
2017-04-27 19:04:45 +00:00
|
|
|
if (_loadRequestId || _allLoaded) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-27 08:02:56 +00:00
|
|
|
_loadRequestId = _api.request(MTPmessages_Search(
|
2017-11-20 19:54:05 +00:00
|
|
|
MTP_flags(0),
|
|
|
|
MTP_inputPeerEmpty(),
|
2021-08-25 08:15:05 +00:00
|
|
|
MTP_string(), // q
|
2020-10-06 12:58:16 +00:00
|
|
|
MTP_inputPeerEmpty(),
|
2020-09-01 06:44:18 +00:00
|
|
|
MTPint(), // top_msg_id
|
2017-11-20 19:54:05 +00:00
|
|
|
MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)),
|
2021-08-25 08:15:05 +00:00
|
|
|
MTP_int(0), // min_date
|
|
|
|
MTP_int(0), // max_date
|
2017-11-20 19:54:05 +00:00
|
|
|
MTP_int(_offsetId),
|
2021-08-25 08:15:05 +00:00
|
|
|
MTP_int(0), // add_offset
|
2017-11-20 19:54:05 +00:00
|
|
|
MTP_int(_offsetId ? kFirstPageCount : kPerPageCount),
|
2021-08-25 08:15:05 +00:00
|
|
|
MTP_int(0), // max_id
|
|
|
|
MTP_int(0), // min_id
|
|
|
|
MTP_long(0) // hash
|
2017-11-20 19:54:05 +00:00
|
|
|
)).done([this](const MTPmessages_Messages &result) {
|
2017-04-27 19:04:45 +00:00
|
|
|
_loadRequestId = 0;
|
|
|
|
|
2019-01-18 12:27:37 +00:00
|
|
|
auto handleResult = [&](auto &data) {
|
2019-07-25 18:55:11 +00:00
|
|
|
session().data().processUsers(data.vusers());
|
|
|
|
session().data().processChats(data.vchats());
|
2019-07-05 13:38:38 +00:00
|
|
|
receivedCalls(data.vmessages().v);
|
2017-04-27 19:04:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2017-11-20 19:54:05 +00:00
|
|
|
case mtpc_messages_messagesNotModified: {
|
|
|
|
LOG(("API Error: received messages.messagesNotModified! (Calls::BoxController::preloadRows)"));
|
|
|
|
} break;
|
2017-04-27 19:04:45 +00:00
|
|
|
default: Unexpected("Type of messages.Messages (Calls::BoxController::preloadRows)");
|
|
|
|
}
|
2021-11-26 20:46:53 +00:00
|
|
|
}).fail([this] {
|
2017-04-27 19:04:45 +00:00
|
|
|
_loadRequestId = 0;
|
|
|
|
}).send();
|
|
|
|
}
|
|
|
|
|
2021-01-21 15:57:12 +00:00
|
|
|
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);
|
|
|
|
|
2021-12-09 17:56:24 +00:00
|
|
|
auto result = base::make_unique_q<Ui::PopupMenu>(
|
|
|
|
parent,
|
|
|
|
st::popupMenuWithIcons);
|
2021-01-21 15:57:12 +00:00
|
|
|
result->addAction(tr::lng_context_delete_selected(tr::now), [=] {
|
2021-06-13 07:37:52 +00:00
|
|
|
_window->show(
|
2023-05-02 09:33:19 +00:00
|
|
|
Box<DeleteMessagesBox>(session, base::duplicate(ids)));
|
2021-12-09 17:56:24 +00:00
|
|
|
}, &st::menuIconDelete);
|
2021-01-21 15:57:12 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
void BoxController::refreshAbout() {
|
2019-06-19 15:09:03 +00:00
|
|
|
setDescriptionText(delegate()->peerListFullRowsCount() ? QString() : tr::lng_call_box_about(tr::now));
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 08:31:24 +00:00
|
|
|
void BoxController::rowClicked(not_null<PeerListRow*> row) {
|
2020-06-11 16:33:15 +00:00
|
|
|
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);
|
2017-10-03 13:05:58 +00:00
|
|
|
});
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 12:37:38 +00:00
|
|
|
void BoxController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
2017-04-27 19:04:45 +00:00
|
|
|
auto user = row->peer()->asUser();
|
2017-08-17 09:06:26 +00:00
|
|
|
Assert(user != nullptr);
|
2017-04-27 19:04:45 +00:00
|
|
|
|
2020-07-31 17:36:20 +00:00
|
|
|
Core::App().calls().startOutgoingCall(user, false);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
|
|
|
|
if (result.empty()) {
|
|
|
|
_allLoaded = true;
|
|
|
|
}
|
|
|
|
|
2019-01-03 12:36:01 +00:00
|
|
|
for (const auto &message : result) {
|
2019-04-25 12:45:15 +00:00
|
|
|
const auto msgId = IdFromMessage(message);
|
|
|
|
const auto peerId = PeerFromMessage(message);
|
2019-07-25 18:55:11 +00:00
|
|
|
if (const auto peer = session().data().peerLoaded(peerId)) {
|
|
|
|
const auto item = session().data().addNewMessage(
|
2019-04-25 12:45:15 +00:00
|
|
|
message,
|
2021-07-28 11:55:02 +00:00
|
|
|
MessageFlags(),
|
2019-04-25 12:45:15 +00:00
|
|
|
NewMessageType::Existing);
|
2017-04-27 21:17:00 +00:00
|
|
|
insertRow(item, InsertWay::Append);
|
2017-04-27 19:04:45 +00:00
|
|
|
} else {
|
2021-04-01 21:04:10 +00:00
|
|
|
LOG(("API Error: a search results with not loaded peer %1"
|
|
|
|
).arg(peerId.value));
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
_offsetId = msgId;
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshAbout();
|
2017-06-13 21:08:47 +00:00
|
|
|
delegate()->peerListRefreshRows();
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-10-05 15:35:52 +00:00
|
|
|
bool BoxController::insertRow(
|
|
|
|
not_null<HistoryItem*> item,
|
|
|
|
InsertWay way) {
|
2017-04-27 19:04:45 +00:00
|
|
|
if (auto row = rowForItem(item)) {
|
2017-04-27 21:17:00 +00:00
|
|
|
if (row->canAddItem(item)) {
|
|
|
|
row->addItem(item);
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2017-10-05 15:35:52 +00:00
|
|
|
(way == InsertWay::Append)
|
|
|
|
? delegate()->peerListAppendRow(createRow(item))
|
|
|
|
: delegate()->peerListPrependRow(createRow(item));
|
2017-10-22 17:06:57 +00:00
|
|
|
delegate()->peerListSortRows([](
|
|
|
|
const PeerListRow &a,
|
|
|
|
const PeerListRow &b) {
|
|
|
|
return static_cast<const Row&>(a).maxItemId()
|
|
|
|
> static_cast<const Row&>(b).maxItemId();
|
2017-04-27 19:04:45 +00:00
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-05 15:35:52 +00:00
|
|
|
BoxController::Row *BoxController::rowForItem(not_null<const HistoryItem*> item) {
|
2017-06-13 21:08:47 +00:00
|
|
|
auto v = delegate();
|
|
|
|
if (auto fullRowsCount = v->peerListFullRowsCount()) {
|
2017-04-27 19:04:45 +00:00
|
|
|
auto itemId = item->id;
|
2017-06-13 21:08:47 +00:00
|
|
|
auto lastRow = static_cast<Row*>(v->peerListRowAt(fullRowsCount - 1).get());
|
2017-04-27 19:04:45 +00:00
|
|
|
if (itemId < lastRow->minItemId()) {
|
2017-04-27 21:17:00 +00:00
|
|
|
return lastRow;
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
2017-06-13 21:08:47 +00:00
|
|
|
auto firstRow = static_cast<Row*>(v->peerListRowAt(0).get());
|
2017-04-27 19:04:45 +00:00
|
|
|
if (itemId > firstRow->maxItemId()) {
|
2017-04-27 21:17:00 +00:00
|
|
|
return firstRow;
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2017-06-13 21:08:47 +00:00
|
|
|
auto middleRow = static_cast<Row*>(v->peerListRowAt(middle).get());
|
2017-04-27 19:04:45 +00:00
|
|
|
if (middleRow->maxItemId() >= itemId) {
|
|
|
|
left = middle;
|
|
|
|
} else {
|
|
|
|
right = middle;
|
|
|
|
}
|
|
|
|
}
|
2017-06-13 21:08:47 +00:00
|
|
|
auto result = static_cast<Row*>(v->peerListRowAt(left).get());
|
2017-04-27 19:04:45 +00:00
|
|
|
// 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) {
|
2017-06-13 21:08:47 +00:00
|
|
|
auto possibleResult = static_cast<Row*>(v->peerListRowAt(left + 1).get());
|
2017-08-17 09:06:26 +00:00
|
|
|
Assert(possibleResult->maxItemId() < itemId);
|
2017-04-27 19:04:45 +00:00
|
|
|
if (possibleResult->canAddItem(item)) {
|
|
|
|
return possibleResult;
|
|
|
|
}
|
|
|
|
}
|
2017-04-27 21:17:00 +00:00
|
|
|
return result;
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-10-05 15:35:52 +00:00
|
|
|
std::unique_ptr<PeerListRow> BoxController::createRow(
|
|
|
|
not_null<HistoryItem*> item) const {
|
2019-11-12 15:15:34 +00:00
|
|
|
return std::make_unique<Row>(item);
|
2017-04-27 19:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 15:57:12 +00:00
|
|
|
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))
|
2021-01-26 10:28:25 +00:00
|
|
|
)).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();
|
|
|
|
}
|
2021-01-21 15:57:12 +00:00
|
|
|
}
|
2021-01-26 10:28:25 +00:00
|
|
|
});
|
2021-01-21 15:57:12 +00:00
|
|
|
}).send();
|
|
|
|
};
|
|
|
|
|
|
|
|
box->addButton(tr::lng_call_box_clear_button(), [=] {
|
|
|
|
sendRequest(revokeCheckbox->checked(), sendRequest);
|
|
|
|
});
|
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
}
|
|
|
|
|
2017-04-27 19:04:45 +00:00
|
|
|
} // namespace Calls
|