mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add a nice countdown to scheduled voice chat panel.
This commit is contained in:
parent
66e7f05df1
commit
d7e90fec1a
8 changed files with 293 additions and 108 deletions
|
@ -1098,8 +1098,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_invite_user_chat" = "the voice chat";
|
"lng_action_invite_user_chat" = "the voice chat";
|
||||||
"lng_action_invite_users_and_one" = "{accumulated}, {user}";
|
"lng_action_invite_users_and_one" = "{accumulated}, {user}";
|
||||||
"lng_action_invite_users_and_last" = "{accumulated} and {user}";
|
"lng_action_invite_users_and_last" = "{accumulated} and {user}";
|
||||||
"lng_action_group_call_started" = "{from} started {chat}";
|
"lng_action_group_call_started_group" = "{from} started a voice chat";
|
||||||
"lng_action_group_call_started_chat" = "a voice chat";
|
"lng_action_group_call_started_channel" = "Voice chat started";
|
||||||
|
"lng_action_group_call_scheduled_group" = "{from} scheduled a voice chat";
|
||||||
|
"lng_action_group_call_scheduled_channel" = "Voice chat scheduled";
|
||||||
"lng_action_group_call_finished" = "Voice chat finished ({duration})";
|
"lng_action_group_call_finished" = "Voice chat finished ({duration})";
|
||||||
"lng_action_add_user" = "{from} added {user}";
|
"lng_action_add_user" = "{from} added {user}";
|
||||||
"lng_action_add_users_many" = "{from} added {users}";
|
"lng_action_add_users_many" = "{from} added {users}";
|
||||||
|
@ -2073,6 +2075,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_call_starts_tomorrow" = "tomorrow at {time}";
|
"lng_group_call_starts_tomorrow" = "tomorrow at {time}";
|
||||||
"lng_group_call_starts_date" = "{date} at {time}";
|
"lng_group_call_starts_date" = "{date} at {time}";
|
||||||
"lng_group_call_starts_in" = "Starts in";
|
"lng_group_call_starts_in" = "Starts in";
|
||||||
|
"lng_group_call_starts_short_today" = "Today, {time}";
|
||||||
|
"lng_group_call_starts_short_tomorrow" = "Tomorrow, {time}";
|
||||||
|
"lng_group_call_starts_short_date" = "{date}, {time}";
|
||||||
"lng_group_call_start_now" = "Start Now";
|
"lng_group_call_start_now" = "Start Now";
|
||||||
"lng_group_call_set_reminder" = "Set Reminder";
|
"lng_group_call_set_reminder" = "Set Reminder";
|
||||||
"lng_group_call_cancel_reminder" = "Cancel Reminder";
|
"lng_group_call_cancel_reminder" = "Cancel Reminder";
|
||||||
|
|
|
@ -945,3 +945,18 @@ callTopBarMuteCrossLine: CrossLineAnimation {
|
||||||
endPosition: point(26px, 23px);
|
endPosition: point(26px, 23px);
|
||||||
stroke: 2px;
|
stroke: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupCallStartsIn: FlatLabel(defaultFlatLabel) {
|
||||||
|
style: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(20px semibold);
|
||||||
|
linkFont: font(20px semibold);
|
||||||
|
linkFontOver: font(20px semibold underline);
|
||||||
|
}
|
||||||
|
textFg: groupCallMembersFg;
|
||||||
|
}
|
||||||
|
groupCallScheduledBodyHeight: 200px;
|
||||||
|
groupCallStartsWhen: groupCallStartsIn;
|
||||||
|
groupCallStartsInTop: 10px;
|
||||||
|
groupCallStartsWhenTop: 160px;
|
||||||
|
groupCallCountdownFont: font(64px semibold);
|
||||||
|
groupCallCountdownTop: 52px;
|
||||||
|
|
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/dropdown_menu.h"
|
#include "ui/widgets/dropdown_menu.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
|
#include "ui/chat/group_call_bar.h"
|
||||||
#include "ui/layers/layer_manager.h"
|
#include "ui/layers/layer_manager.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
@ -41,6 +42,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peers/add_participants_box.h"
|
#include "boxes/peers/add_participants_box.h"
|
||||||
#include "boxes/peer_lists_box.h"
|
#include "boxes/peer_lists_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "base/timer_rpl.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "apiwrap.h" // api().kickParticipant.
|
#include "apiwrap.h" // api().kickParticipant.
|
||||||
#include "styles/style_calls.h"
|
#include "styles/style_calls.h"
|
||||||
|
@ -116,6 +119,122 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<QString> StartsWhenText(
|
||||||
|
rpl::producer<TimeId> date) {
|
||||||
|
return std::move(
|
||||||
|
date
|
||||||
|
) | rpl::map([](TimeId date) -> rpl::producer<QString> {
|
||||||
|
const auto parsedDate = base::unixtime::parse(date);
|
||||||
|
const auto dateDay = QDateTime(parsedDate.date(), QTime(0, 0));
|
||||||
|
const auto previousDay = QDateTime(
|
||||||
|
parsedDate.date().addDays(-1),
|
||||||
|
QTime(0, 0));
|
||||||
|
const auto now = QDateTime::currentDateTime();
|
||||||
|
const auto kDay = int64(24 * 60 * 60);
|
||||||
|
const auto tillTomorrow = int64(now.secsTo(previousDay));
|
||||||
|
const auto tillToday = tillTomorrow + kDay;
|
||||||
|
const auto tillAfter = tillToday + kDay;
|
||||||
|
|
||||||
|
const auto time = parsedDate.time().toString(
|
||||||
|
QLocale::system().timeFormat(QLocale::ShortFormat));
|
||||||
|
auto exact = tr::lng_group_call_starts_short_date(
|
||||||
|
lt_date,
|
||||||
|
rpl::single(langDayOfMonthFull(dateDay.date())),
|
||||||
|
lt_time,
|
||||||
|
rpl::single(time));
|
||||||
|
auto tomorrow = tr::lng_group_call_starts_short_tomorrow(
|
||||||
|
lt_time,
|
||||||
|
rpl::single(time));
|
||||||
|
auto today = tr::lng_group_call_starts_short_today(
|
||||||
|
lt_time,
|
||||||
|
rpl::single(time));
|
||||||
|
|
||||||
|
auto todayAndAfter = rpl::single(
|
||||||
|
std::move(today)
|
||||||
|
) | rpl::then(base::timer_once(
|
||||||
|
std::min(tillAfter, kDay) * crl::time(1000)
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return rpl::duplicate(exact);
|
||||||
|
})) | rpl::flatten_latest();
|
||||||
|
|
||||||
|
auto tomorrowAndAfter = rpl::single(
|
||||||
|
std::move(tomorrow)
|
||||||
|
) | rpl::then(base::timer_once(
|
||||||
|
std::min(tillToday, kDay) * crl::time(1000)
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return rpl::duplicate(todayAndAfter);
|
||||||
|
})) | rpl::flatten_latest();
|
||||||
|
|
||||||
|
auto full = rpl::single(
|
||||||
|
rpl::duplicate(exact)
|
||||||
|
) | rpl::then(base::timer_once(
|
||||||
|
tillTomorrow * crl::time(1000)
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return rpl::duplicate(tomorrowAndAfter);
|
||||||
|
})) | rpl::flatten_latest();
|
||||||
|
|
||||||
|
if (tillTomorrow > 0) {
|
||||||
|
return std::move(full);
|
||||||
|
} else if (tillToday > 0) {
|
||||||
|
return std::move(tomorrowAndAfter);
|
||||||
|
} else if (tillAfter > 0) {
|
||||||
|
return std::move(todayAndAfter);
|
||||||
|
} else {
|
||||||
|
return std::move(exact);
|
||||||
|
}
|
||||||
|
}) | rpl::flatten_latest();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::RpWidget> CreateGradientLabel(
|
||||||
|
QWidget *parent,
|
||||||
|
rpl::producer<QString> text) {
|
||||||
|
struct State {
|
||||||
|
QBrush brush;
|
||||||
|
QPainterPath path;
|
||||||
|
};
|
||||||
|
auto result = object_ptr<Ui::RpWidget>(parent);
|
||||||
|
const auto raw = result.data();
|
||||||
|
const auto state = raw->lifetime().make_state<State>();
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
text
|
||||||
|
) | rpl::start_with_next([=](const QString &text) {
|
||||||
|
state->path = QPainterPath();
|
||||||
|
const auto &font = st::groupCallCountdownFont;
|
||||||
|
state->path.addText(0, font->ascent, font->f, text);
|
||||||
|
const auto width = font->width(text);
|
||||||
|
raw->resize(width, font->height);
|
||||||
|
auto gradient = QLinearGradient(QPoint(width, 0), QPoint());
|
||||||
|
gradient.setStops(QGradientStops{
|
||||||
|
{ 0.0, st::groupCallForceMutedBar1->c },
|
||||||
|
{ .7, st::groupCallForceMutedBar2->c },
|
||||||
|
{ 1.0, st::groupCallForceMutedBar3->c }
|
||||||
|
});
|
||||||
|
state->brush = QBrush(std::move(gradient));
|
||||||
|
raw->update();
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
raw->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(raw);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
const auto skip = st::groupCallWidth / 20;
|
||||||
|
const auto available = parent->width() - 2 * skip;
|
||||||
|
const auto full = raw->width();
|
||||||
|
if (available > 0 && full > available) {
|
||||||
|
const auto scale = available / float64(full);
|
||||||
|
const auto shift = raw->rect().center();
|
||||||
|
p.translate(shift);
|
||||||
|
p.scale(scale, scale);
|
||||||
|
p.translate(-shift);
|
||||||
|
}
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(state->brush);
|
||||||
|
p.drawPath(state->path);
|
||||||
|
}, raw->lifetime());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSectionSubtitle(
|
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSectionSubtitle(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<QString> text) {
|
rpl::producer<QString> text) {
|
||||||
|
@ -451,18 +570,23 @@ void Panel::initControls() {
|
||||||
});
|
});
|
||||||
|
|
||||||
_settings->setText(tr::lng_group_call_settings());
|
_settings->setText(tr::lng_group_call_settings());
|
||||||
const auto scheduled = (_call->scheduleDate() != 0);
|
const auto scheduleDate = _call->scheduleDate();
|
||||||
_hangup->setText(scheduled
|
_hangup->setText(scheduleDate
|
||||||
? tr::lng_group_call_close()
|
? tr::lng_group_call_close()
|
||||||
: tr::lng_group_call_leave());
|
: tr::lng_group_call_leave());
|
||||||
if (scheduled) {
|
if (scheduleDate) {
|
||||||
_call->real(
|
auto changes = _call->real(
|
||||||
) | rpl::map([=](not_null<Data::GroupCall*> real) {
|
) | rpl::map([=](not_null<Data::GroupCall*> real) {
|
||||||
return real->scheduleDateValue();
|
return real->scheduleDateValue();
|
||||||
}) | rpl::flatten_latest() | rpl::filter([](TimeId date) {
|
}) | rpl::flatten_latest();
|
||||||
|
setupScheduledLabels(rpl::single(
|
||||||
|
scheduleDate
|
||||||
|
) | rpl::then(rpl::duplicate(changes)));
|
||||||
|
std::move(changes) | rpl::filter([](TimeId date) {
|
||||||
return (date == 0);
|
return (date == 0);
|
||||||
}) | rpl::take(1) | rpl::start_with_next([=] {
|
}) | rpl::take(1) | rpl::start_with_next([=] {
|
||||||
_hangup->setText(tr::lng_group_call_leave());
|
_hangup->setText(tr::lng_group_call_leave());
|
||||||
|
setupMembers();
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,8 +677,66 @@ void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::setupScheduledLabels(rpl::producer<TimeId> date) {
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
_startsIn.create(
|
||||||
|
widget(),
|
||||||
|
tr::lng_group_call_starts_in(),
|
||||||
|
st::groupCallStartsIn);
|
||||||
|
date = std::move(date) | rpl::take_while(_1 != 0);
|
||||||
|
_startsWhen.create(
|
||||||
|
widget(),
|
||||||
|
StartsWhenText(rpl::duplicate(date)),
|
||||||
|
st::groupCallStartsWhen);
|
||||||
|
_countdown = CreateGradientLabel(widget(), std::move(
|
||||||
|
date
|
||||||
|
) | rpl::map([=](TimeId date) {
|
||||||
|
_countdownData = std::make_shared<Ui::GroupCallScheduledLeft>(date);
|
||||||
|
return _countdownData->text();
|
||||||
|
}) | rpl::flatten_latest());
|
||||||
|
|
||||||
|
const auto top = [=] {
|
||||||
|
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
|
||||||
|
const auto membersTop = st::groupCallMembersTop;
|
||||||
|
const auto height = st::groupCallScheduledBodyHeight;
|
||||||
|
return (membersTop + (muteTop - membersTop - height) / 2);
|
||||||
|
};
|
||||||
|
rpl::combine(
|
||||||
|
widget()->sizeValue(),
|
||||||
|
_startsIn->widthValue()
|
||||||
|
) | rpl::start_with_next([=](QSize size, int width) {
|
||||||
|
_startsIn->move(
|
||||||
|
(size.width() - width) / 2,
|
||||||
|
top() + st::groupCallStartsInTop);
|
||||||
|
}, _startsIn->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
widget()->sizeValue(),
|
||||||
|
_startsWhen->widthValue()
|
||||||
|
) | rpl::start_with_next([=](QSize size, int width) {
|
||||||
|
_startsWhen->move(
|
||||||
|
(size.width() - width) / 2,
|
||||||
|
top() + st::groupCallStartsWhenTop);
|
||||||
|
}, _startsWhen->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
widget()->sizeValue(),
|
||||||
|
_countdown->widthValue()
|
||||||
|
) | rpl::start_with_next([=](QSize size, int width) {
|
||||||
|
_countdown->move(
|
||||||
|
(size.width() - width) / 2,
|
||||||
|
top() + st::groupCallCountdownTop);
|
||||||
|
}, _startsWhen->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::setupMembers() {
|
void Panel::setupMembers() {
|
||||||
Expects(!_members);
|
if (_members) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startsIn.destroy();
|
||||||
|
_countdown.destroy();
|
||||||
|
_startsWhen.destroy();
|
||||||
|
|
||||||
_members.create(widget(), _call);
|
_members.create(widget(), _call);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ class Window;
|
||||||
class ScrollArea;
|
class ScrollArea;
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
class LayerManager;
|
class LayerManager;
|
||||||
|
class GroupCallScheduledLeft;
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
class TitleControls;
|
class TitleControls;
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
@ -75,6 +76,7 @@ private:
|
||||||
void initControls();
|
void initControls();
|
||||||
void initLayout();
|
void initLayout();
|
||||||
void initGeometry();
|
void initGeometry();
|
||||||
|
void setupScheduledLabels(rpl::producer<TimeId> date);
|
||||||
void setupMembers();
|
void setupMembers();
|
||||||
void setupJoinAsChangedToasts();
|
void setupJoinAsChangedToasts();
|
||||||
void setupTitleChangedToasts();
|
void setupTitleChangedToasts();
|
||||||
|
@ -124,6 +126,7 @@ private:
|
||||||
object_ptr<Members> _members = { nullptr };
|
object_ptr<Members> _members = { nullptr };
|
||||||
object_ptr<Ui::FlatLabel> _startsIn = { nullptr };
|
object_ptr<Ui::FlatLabel> _startsIn = { nullptr };
|
||||||
object_ptr<Ui::RpWidget> _countdown = { nullptr };
|
object_ptr<Ui::RpWidget> _countdown = { nullptr };
|
||||||
|
std::shared_ptr<Ui::GroupCallScheduledLeft> _countdownData;
|
||||||
object_ptr<Ui::FlatLabel> _startsWhen = { nullptr };
|
object_ptr<Ui::FlatLabel> _startsWhen = { nullptr };
|
||||||
ChooseJoinAsProcess _joinAsProcess;
|
ChooseJoinAsProcess _joinAsProcess;
|
||||||
|
|
||||||
|
|
|
@ -72,17 +72,6 @@ constexpr auto kPinnedMessageTextLimit = 16;
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<bool> PeerHasThisCall(
|
|
||||||
not_null<PeerData*> peer,
|
|
||||||
uint64 id) {
|
|
||||||
const auto call = peer->groupCall();
|
|
||||||
return call
|
|
||||||
? std::make_optional(call->id() == id)
|
|
||||||
: PeerCallKnown(peer)
|
|
||||||
? std::make_optional(false)
|
|
||||||
: std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] uint64 CallIdFromInput(const MTPInputGroupCall &data) {
|
[[nodiscard]] uint64 CallIdFromInput(const MTPInputGroupCall &data) {
|
||||||
return data.match([&](const MTPDinputGroupCall &data) {
|
return data.match([&](const MTPDinputGroupCall &data) {
|
||||||
return data.vid().v;
|
return data.vid().v;
|
||||||
|
@ -348,14 +337,30 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
|
|
||||||
auto prepareGroupCall = [this](const MTPDmessageActionGroupCall &action) {
|
auto prepareGroupCall = [this](const MTPDmessageActionGroupCall &action) {
|
||||||
if (const auto duration = action.vduration()) {
|
if (const auto duration = action.vduration()) {
|
||||||
return prepareDiscardedCallText(duration->v);
|
const auto seconds = duration->v;
|
||||||
|
const auto days = seconds / 86400;
|
||||||
|
const auto hours = seconds / 3600;
|
||||||
|
const auto minutes = seconds / 60;
|
||||||
|
auto text = (days > 1)
|
||||||
|
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
|
||||||
|
: (hours > 1)
|
||||||
|
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
|
||||||
|
: (minutes > 1)
|
||||||
|
? tr::lng_group_call_duration_minutes(tr::now, lt_count, minutes)
|
||||||
|
: tr::lng_group_call_duration_seconds(tr::now, lt_count, seconds);
|
||||||
|
return PreparedText{ tr::lng_action_group_call_finished(tr::now, lt_duration, text) };
|
||||||
}
|
}
|
||||||
const auto callId = CallIdFromInput(action.vcall());
|
auto result = PreparedText{};
|
||||||
const auto peer = history()->peer;
|
if (history()->peer->isBroadcast()) {
|
||||||
const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false)
|
result.text = tr::lng_action_group_call_started_channel(tr::now);
|
||||||
? callId
|
} else {
|
||||||
: 0;
|
result.links.push_back(fromLink());
|
||||||
return prepareStartedCallText(linkCallId);
|
result.text = tr::lng_action_group_call_started_group(
|
||||||
|
tr::now,
|
||||||
|
lt_from,
|
||||||
|
fromLinkText());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto prepareInviteToGroupCall = [this](const MTPDmessageActionInviteToGroupCall &action) {
|
auto prepareInviteToGroupCall = [this](const MTPDmessageActionInviteToGroupCall &action) {
|
||||||
|
@ -406,12 +411,17 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
};
|
};
|
||||||
|
|
||||||
auto prepareGroupCallScheduled = [this](const MTPDmessageActionGroupCallScheduled &action) {
|
auto prepareGroupCallScheduled = [this](const MTPDmessageActionGroupCallScheduled &action) {
|
||||||
const auto callId = CallIdFromInput(action.vcall());
|
auto result = PreparedText{};
|
||||||
const auto peer = history()->peer;
|
if (history()->peer->isBroadcast()) {
|
||||||
const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false)
|
result.text = tr::lng_action_group_call_scheduled_channel(tr::now);
|
||||||
? callId
|
} else {
|
||||||
: 0;
|
result.links.push_back(fromLink());
|
||||||
return prepareStartedCallText(linkCallId);
|
result.text = tr::lng_action_group_call_scheduled_group(
|
||||||
|
tr::now,
|
||||||
|
lt_from,
|
||||||
|
fromLinkText());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto messageText = action.match([&](
|
const auto messageText = action.match([&](
|
||||||
|
@ -577,41 +587,6 @@ bool HistoryService::updateDependent(bool force) {
|
||||||
return (dependent->msg || !dependent->msgId);
|
return (dependent->msg || !dependent->msgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryService::PreparedText HistoryService::prepareDiscardedCallText(
|
|
||||||
int duration) {
|
|
||||||
const auto seconds = duration;
|
|
||||||
const auto days = seconds / 86400;
|
|
||||||
const auto hours = seconds / 3600;
|
|
||||||
const auto minutes = seconds / 60;
|
|
||||||
auto text = (days > 1)
|
|
||||||
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
|
|
||||||
: (hours > 1)
|
|
||||||
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
|
|
||||||
: (minutes > 1)
|
|
||||||
? tr::lng_group_call_duration_minutes(tr::now, lt_count, minutes)
|
|
||||||
: tr::lng_group_call_duration_seconds(tr::now, lt_count, seconds);
|
|
||||||
return PreparedText{ tr::lng_action_group_call_finished(tr::now, lt_duration, text) };
|
|
||||||
}
|
|
||||||
|
|
||||||
HistoryService::PreparedText HistoryService::prepareStartedCallText(
|
|
||||||
uint64 linkCallId) {
|
|
||||||
auto result = PreparedText{};
|
|
||||||
result.links.push_back(fromLink());
|
|
||||||
auto chatText = tr::lng_action_group_call_started_chat(tr::now);
|
|
||||||
if (linkCallId) {
|
|
||||||
const auto peer = history()->peer;
|
|
||||||
result.links.push_back(GroupCallClickHandler(peer, linkCallId));
|
|
||||||
chatText = textcmdLink(2, chatText);
|
|
||||||
}
|
|
||||||
result.text = tr::lng_action_group_call_started(
|
|
||||||
tr::now,
|
|
||||||
lt_from,
|
|
||||||
fromLinkText(),
|
|
||||||
lt_chat,
|
|
||||||
chatText);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
|
HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
|
||||||
const QVector<MTPint> &users,
|
const QVector<MTPint> &users,
|
||||||
uint64 linkCallId) {
|
uint64 linkCallId) {
|
||||||
|
@ -973,11 +948,12 @@ void HistoryService::createFromMtp(const MTPDmessage &message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||||
if (message.vaction().type() == mtpc_messageActionGameScore) {
|
const auto type = message.vaction().type();
|
||||||
|
if (type == mtpc_messageActionGameScore) {
|
||||||
const auto &data = message.vaction().c_messageActionGameScore();
|
const auto &data = message.vaction().c_messageActionGameScore();
|
||||||
UpdateComponents(HistoryServiceGameScore::Bit());
|
UpdateComponents(HistoryServiceGameScore::Bit());
|
||||||
Get<HistoryServiceGameScore>()->score = data.vscore().v;
|
Get<HistoryServiceGameScore>()->score = data.vscore().v;
|
||||||
} else if (message.vaction().type() == mtpc_messageActionPaymentSent) {
|
} else if (type == mtpc_messageActionPaymentSent) {
|
||||||
const auto &data = message.vaction().c_messageActionPaymentSent();
|
const auto &data = message.vaction().c_messageActionPaymentSent();
|
||||||
UpdateComponents(HistoryServicePayment::Bit());
|
UpdateComponents(HistoryServicePayment::Bit());
|
||||||
const auto amount = data.vtotal_amount().v;
|
const auto amount = data.vtotal_amount().v;
|
||||||
|
@ -998,36 +974,24 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||||
crl::guard(weak, [=] { weak->window().activate(); }));
|
crl::guard(weak, [=] { weak->window().activate(); }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (message.vaction().type() == mtpc_messageActionGroupCall) {
|
} else if (type == mtpc_messageActionGroupCall
|
||||||
const auto &data = message.vaction().c_messageActionGroupCall();
|
|| type == mtpc_messageActionGroupCallScheduled) {
|
||||||
if (data.vduration()) {
|
const auto started = (type == mtpc_messageActionGroupCall);
|
||||||
|
const auto &callData = started
|
||||||
|
? message.vaction().c_messageActionGroupCall().vcall()
|
||||||
|
: message.vaction().c_messageActionGroupCallScheduled().vcall();
|
||||||
|
const auto duration = started
|
||||||
|
? message.vaction().c_messageActionGroupCall().vduration()
|
||||||
|
: tl::conditional<MTPint>();
|
||||||
|
if (duration) {
|
||||||
RemoveComponents(HistoryServiceOngoingCall::Bit());
|
RemoveComponents(HistoryServiceOngoingCall::Bit());
|
||||||
} else {
|
} else {
|
||||||
UpdateComponents(HistoryServiceOngoingCall::Bit());
|
UpdateComponents(HistoryServiceOngoingCall::Bit());
|
||||||
const auto call = Get<HistoryServiceOngoingCall>();
|
const auto call = Get<HistoryServiceOngoingCall>();
|
||||||
const auto id = CallIdFromInput(data.vcall());
|
call->id = CallIdFromInput(callData);
|
||||||
call->lifetime.destroy();
|
call->link = GroupCallClickHandler(history()->peer, call->id);
|
||||||
|
|
||||||
const auto peer = history()->peer;
|
|
||||||
const auto has = PeerHasThisCall(peer, id);
|
|
||||||
if (!has.has_value()) {
|
|
||||||
PeerHasThisCallValue(
|
|
||||||
peer,
|
|
||||||
id
|
|
||||||
) | rpl::start_with_next([=](bool has) {
|
|
||||||
updateText(prepareStartedCallText(has ? id : 0));
|
|
||||||
}, call->lifetime);
|
|
||||||
} else if (*has) {
|
|
||||||
PeerHasThisCallValue(
|
|
||||||
peer,
|
|
||||||
id
|
|
||||||
) | rpl::skip(1) | rpl::start_with_next([=](bool has) {
|
|
||||||
Assert(!has);
|
|
||||||
updateText(prepareStartedCallText(0));
|
|
||||||
}, call->lifetime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (message.vaction().type() == mtpc_messageActionInviteToGroupCall) {
|
} else if (type == mtpc_messageActionInviteToGroupCall) {
|
||||||
const auto &data = message.vaction().c_messageActionInviteToGroupCall();
|
const auto &data = message.vaction().c_messageActionInviteToGroupCall();
|
||||||
const auto id = CallIdFromInput(data.vcall());
|
const auto id = CallIdFromInput(data.vcall());
|
||||||
const auto peer = history()->peer;
|
const auto peer = history()->peer;
|
||||||
|
@ -1044,6 +1008,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||||
} else {
|
} else {
|
||||||
UpdateComponents(HistoryServiceOngoingCall::Bit());
|
UpdateComponents(HistoryServiceOngoingCall::Bit());
|
||||||
const auto call = Get<HistoryServiceOngoingCall>();
|
const auto call = Get<HistoryServiceOngoingCall>();
|
||||||
|
call->id = id;
|
||||||
call->lifetime.destroy();
|
call->lifetime.destroy();
|
||||||
|
|
||||||
const auto users = data.vusers().v;
|
const auto users = data.vusers().v;
|
||||||
|
@ -1207,3 +1172,14 @@ not_null<HistoryService*> GenerateJoinedMessage(
|
||||||
GenerateJoinedText(history, inviter),
|
GenerateJoinedText(history, inviter),
|
||||||
flags);
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<bool> PeerHasThisCall(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
uint64 id) {
|
||||||
|
const auto call = peer->groupCall();
|
||||||
|
return call
|
||||||
|
? std::make_optional(call->id() == id)
|
||||||
|
: PeerCallKnown(peer)
|
||||||
|
? std::make_optional(false)
|
||||||
|
: std::nullopt;
|
||||||
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ struct HistoryServiceSelfDestruct
|
||||||
struct HistoryServiceOngoingCall
|
struct HistoryServiceOngoingCall
|
||||||
: public RuntimeComponent<HistoryServiceOngoingCall, HistoryItem> {
|
: public RuntimeComponent<HistoryServiceOngoingCall, HistoryItem> {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
|
ClickHandlerPtr link;
|
||||||
rpl::lifetime lifetime;
|
rpl::lifetime lifetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -160,8 +161,6 @@ private:
|
||||||
PreparedText preparePinnedText();
|
PreparedText preparePinnedText();
|
||||||
PreparedText prepareGameScoreText();
|
PreparedText prepareGameScoreText();
|
||||||
PreparedText preparePaymentSentText();
|
PreparedText preparePaymentSentText();
|
||||||
PreparedText prepareDiscardedCallText(int duration);
|
|
||||||
PreparedText prepareStartedCallText(uint64 linkCallId);
|
|
||||||
PreparedText prepareInvitedToCallText(
|
PreparedText prepareInvitedToCallText(
|
||||||
const QVector<MTPint> &users,
|
const QVector<MTPint> &users,
|
||||||
uint64 linkCallId);
|
uint64 linkCallId);
|
||||||
|
@ -170,8 +169,11 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
not_null<HistoryService*> GenerateJoinedMessage(
|
[[nodiscard]] not_null<HistoryService*> GenerateJoinedMessage(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
TimeId inviteDate,
|
TimeId inviteDate,
|
||||||
not_null<UserData*> inviter,
|
not_null<UserData*> inviter,
|
||||||
MTPDmessage::Flags flags);
|
MTPDmessage::Flags flags);
|
||||||
|
[[nodiscard]] std::optional<bool> PeerHasThisCall(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
uint64 id);
|
||||||
|
|
|
@ -513,17 +513,18 @@ TextState Service::textState(QPoint point, StateRequest request) const {
|
||||||
point - trect.topLeft(),
|
point - trect.topLeft(),
|
||||||
trect.width(),
|
trect.width(),
|
||||||
textRequest));
|
textRequest));
|
||||||
if (auto gamescore = item->Get<HistoryServiceGameScore>()) {
|
if (!result.link
|
||||||
if (!result.link
|
&& result.cursor == CursorState::Text
|
||||||
&& result.cursor == CursorState::Text
|
&& g.contains(point)) {
|
||||||
&& g.contains(point)) {
|
if (const auto gamescore = item->Get<HistoryServiceGameScore>()) {
|
||||||
result.link = gamescore->lnk;
|
result.link = gamescore->lnk;
|
||||||
}
|
} else if (const auto payment = item->Get<HistoryServicePayment>()) {
|
||||||
} else if (auto payment = item->Get<HistoryServicePayment>()) {
|
|
||||||
if (!result.link
|
|
||||||
&& result.cursor == CursorState::Text
|
|
||||||
&& g.contains(point)) {
|
|
||||||
result.link = payment->invoiceLink;
|
result.link = payment->invoiceLink;
|
||||||
|
} else if (const auto call = item->Get<HistoryServiceOngoingCall>()) {
|
||||||
|
const auto peer = history()->peer;
|
||||||
|
if (PeerHasThisCall(peer, call->id).value_or(false)) {
|
||||||
|
result.link = call->link;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (media) {
|
} else if (media) {
|
||||||
|
|
|
@ -152,10 +152,10 @@ void GroupCallBar::refreshOpenBrush() {
|
||||||
if (_openBrushForWidth == width) {
|
if (_openBrushForWidth == width) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto gradient = QLinearGradient(QPoint(width, 0), QPoint(-width, 0));
|
auto gradient = QLinearGradient(QPoint(width, 0), QPoint(0, 0));
|
||||||
gradient.setStops(QGradientStops{
|
gradient.setStops(QGradientStops{
|
||||||
{ 0.0, st::groupCallForceMutedBar1->c },
|
{ 0.0, st::groupCallForceMutedBar1->c },
|
||||||
{ .35, st::groupCallForceMutedBar2->c },
|
{ .7, st::groupCallForceMutedBar2->c },
|
||||||
{ 1.0, st::groupCallForceMutedBar3->c }
|
{ 1.0, st::groupCallForceMutedBar3->c }
|
||||||
});
|
});
|
||||||
_openBrushOverride = QBrush(std::move(gradient));
|
_openBrushOverride = QBrush(std::move(gradient));
|
||||||
|
@ -169,6 +169,7 @@ void GroupCallBar::refreshScheduledProcess() {
|
||||||
if (_scheduledProcess) {
|
if (_scheduledProcess) {
|
||||||
_scheduledProcess = nullptr;
|
_scheduledProcess = nullptr;
|
||||||
_open = nullptr;
|
_open = nullptr;
|
||||||
|
_openBrushForWidth = 0;
|
||||||
_join = std::make_unique<RoundButton>(
|
_join = std::make_unique<RoundButton>(
|
||||||
_inner.get(),
|
_inner.get(),
|
||||||
tr::lng_group_call_join(),
|
tr::lng_group_call_join(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue