358 lines
9.8 KiB
C++
358 lines
9.8 KiB
C++
/*
|
|
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/group/calls_group_rtmp.h"
|
|
|
|
#include "apiwrap.h"
|
|
#include "calls/group/calls_group_common.h"
|
|
#include "data/data_peer.h"
|
|
#include "lang/lang_keys.h"
|
|
#include "main/main_account.h"
|
|
#include "main/main_session.h"
|
|
#include "settings/settings_common.h" // AddDivider.
|
|
#include "ui/boxes/confirm_box.h"
|
|
#include "ui/layers/generic_box.h"
|
|
#include "ui/text/text_utilities.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/popup_menu.h"
|
|
#include "ui/wrap/vertical_layout.h"
|
|
#include "styles/style_boxes.h"
|
|
#include "styles/style_calls.h"
|
|
#include "styles/style_info.h"
|
|
#include "styles/style_layers.h"
|
|
#include "styles/style_menu_icons.h"
|
|
#include "styles/style_settings.h"
|
|
|
|
#include <QGuiApplication>
|
|
#include <QStyle>
|
|
|
|
namespace Calls::Group {
|
|
namespace {
|
|
|
|
constexpr auto kPasswordCharAmount = 24;
|
|
|
|
void StartWithBox(
|
|
not_null<Ui::GenericBox*> box,
|
|
Fn<void()> done,
|
|
Fn<void()> revoke,
|
|
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
|
|
Fn<void(QString)> showToast,
|
|
rpl::producer<RtmpInfo> &&data) {
|
|
struct State {
|
|
base::unique_qptr<Ui::PopupMenu> menu;
|
|
};
|
|
const auto state = box->lifetime().make_state<State>();
|
|
|
|
StartRtmpProcess::FillRtmpRows(
|
|
box->verticalLayout(),
|
|
true,
|
|
std::move(showBox),
|
|
std::move(showToast),
|
|
std::move(data),
|
|
&st::boxLabel,
|
|
&st::groupCallRtmpShowButton,
|
|
&st::settingsSubsectionTitle,
|
|
&st::attentionBoxButton,
|
|
&st::defaultPopupMenu);
|
|
|
|
box->setTitle(tr::lng_group_call_rtmp_title());
|
|
|
|
Settings::AddDividerText(
|
|
box->verticalLayout(),
|
|
tr::lng_group_call_rtmp_info());
|
|
|
|
box->addButton(tr::lng_group_call_rtmp_start(), done);
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
box->setWidth(st::boxWideWidth);
|
|
{
|
|
const auto top = box->addTopButton(st::infoTopBarMenu);
|
|
top->setClickedCallback([=] {
|
|
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
|
top,
|
|
st::popupMenuWithIcons);
|
|
state->menu->addAction(
|
|
tr::lng_group_invite_context_revoke(tr::now),
|
|
revoke,
|
|
&st::menuIconRemove);
|
|
state->menu->setForcedOrigin(
|
|
Ui::PanelAnimation::Origin::TopRight);
|
|
top->setForceRippled(true);
|
|
const auto raw = state->menu.get();
|
|
raw->setDestroyedCallback([=] {
|
|
if ((state->menu == raw) && top) {
|
|
top->setForceRippled(false);
|
|
}
|
|
});
|
|
state->menu->popup(top->mapToGlobal(top->rect().center()));
|
|
return true;
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
StartRtmpProcess::~StartRtmpProcess() {
|
|
if (_request) {
|
|
_request->peer->session().api().request(_request->id).cancel();
|
|
}
|
|
}
|
|
|
|
void StartRtmpProcess::start(
|
|
not_null<PeerData*> peer,
|
|
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
|
|
Fn<void(QString)> showToast,
|
|
Fn<void(JoinInfo)> done) {
|
|
Expects(done != nullptr);
|
|
|
|
const auto session = &peer->session();
|
|
if (_request) {
|
|
if (_request->peer == peer) {
|
|
_request->showBox = std::move(showBox);
|
|
_request->showToast = std::move(showToast);
|
|
_request->done = std::move(done);
|
|
return;
|
|
}
|
|
session->api().request(_request->id).cancel();
|
|
_request = nullptr;
|
|
}
|
|
_request = std::make_unique<RtmpRequest>(
|
|
RtmpRequest{
|
|
.peer = peer,
|
|
.showBox = std::move(showBox),
|
|
.showToast = std::move(showToast),
|
|
.done = std::move(done) });
|
|
session->account().sessionChanges(
|
|
) | rpl::start_with_next([=] {
|
|
_request = nullptr;
|
|
}, _request->lifetime);
|
|
|
|
requestUrl(false);
|
|
}
|
|
|
|
void StartRtmpProcess::requestUrl(bool revoke) {
|
|
const auto session = &_request->peer->session();
|
|
_request->id = session->api().request(MTPphone_GetGroupCallStreamRtmpUrl(
|
|
_request->peer->input,
|
|
MTP_bool(revoke)
|
|
)).done([=](const MTPphone_GroupCallStreamRtmpUrl &result) {
|
|
auto data = result.match([&](
|
|
const MTPDphone_groupCallStreamRtmpUrl &data) {
|
|
return RtmpInfo{ .url = qs(data.vurl()), .key = qs(data.vkey()) };
|
|
});
|
|
processUrl(std::move(data));
|
|
}).fail([=] {
|
|
_request->showToast(Lang::Hard::ServerError());
|
|
}).send();
|
|
}
|
|
|
|
void StartRtmpProcess::processUrl(RtmpInfo data) {
|
|
if (!_request->box) {
|
|
createBox();
|
|
}
|
|
_request->data = std::move(data);
|
|
}
|
|
|
|
void StartRtmpProcess::finish(JoinInfo info) {
|
|
const auto done = std::move(_request->done);
|
|
const auto box = _request->box;
|
|
const auto current = _request->data.current();
|
|
_request = nullptr;
|
|
info.rtmpInfo = current;
|
|
done(std::move(info));
|
|
if (const auto strong = box.data()) {
|
|
strong->closeBox();
|
|
}
|
|
}
|
|
|
|
void StartRtmpProcess::createBox() {
|
|
auto done = [=] {
|
|
const auto peer = _request->peer;
|
|
finish({ .peer = peer, .joinAs = peer, .rtmp = true });
|
|
};
|
|
auto revoke = [=] {
|
|
const auto guard = base::make_weak(&_request->guard);
|
|
_request->showBox(Ui::MakeConfirmBox({
|
|
.text = tr::lng_group_call_rtmp_revoke_sure(),
|
|
.confirmed = crl::guard(guard, [=](Fn<void()> &&close) {
|
|
requestUrl(true);
|
|
close();
|
|
}),
|
|
.confirmText = tr::lng_group_invite_context_revoke(),
|
|
}));
|
|
};
|
|
auto object = Box(
|
|
StartWithBox,
|
|
std::move(done),
|
|
std::move(revoke),
|
|
_request->showBox,
|
|
_request->showToast,
|
|
_request->data.value());
|
|
object->boxClosing(
|
|
) | rpl::start_with_next([=] {
|
|
_request = nullptr;
|
|
}, _request->lifetime);
|
|
_request->box = Ui::MakeWeak(object.data());
|
|
_request->showBox(std::move(object));
|
|
}
|
|
|
|
void StartRtmpProcess::FillRtmpRows(
|
|
not_null<Ui::VerticalLayout*> container,
|
|
bool divider,
|
|
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
|
|
Fn<void(QString)> showToast,
|
|
rpl::producer<RtmpInfo> &&data,
|
|
const style::FlatLabel *labelStyle,
|
|
const style::IconButton *showButtonStyle,
|
|
const style::FlatLabel *subsectionTitleStyle,
|
|
const style::RoundButton *attentionButtonStyle,
|
|
const style::PopupMenu *popupMenuStyle) {
|
|
struct State {
|
|
rpl::variable<bool> hidden = true;
|
|
rpl::variable<QString> key;
|
|
rpl::variable<QString> url;
|
|
bool warned = false;
|
|
};
|
|
|
|
const auto &rowPadding = st::boxRowPadding;
|
|
|
|
const auto passChar = QChar(container->style()->styleHint(
|
|
QStyle::SH_LineEdit_PasswordCharacter));
|
|
const auto state = container->lifetime().make_state<State>();
|
|
state->key = rpl::duplicate(
|
|
data
|
|
) | rpl::map([=](const auto &d) { return d.key; });
|
|
state->url = std::move(
|
|
data
|
|
) | rpl::map([=](const auto &d) { return d.url; });
|
|
|
|
const auto addButton = [&](
|
|
bool key,
|
|
rpl::producer<QString> &&text) {
|
|
auto wrap = object_ptr<Ui::RpWidget>(container);
|
|
auto button = Ui::CreateChild<Ui::RoundButton>(
|
|
wrap.data(),
|
|
rpl::duplicate(text),
|
|
st::groupCallRtmpCopyButton);
|
|
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
|
button->setClickedCallback(key
|
|
? Fn<void()>([=] {
|
|
QGuiApplication::clipboard()->setText(state->key.current());
|
|
showToast(tr::lng_group_call_rtmp_key_copied(tr::now));
|
|
})
|
|
: Fn<void()>([=] {
|
|
QGuiApplication::clipboard()->setText(state->url.current());
|
|
showToast(tr::lng_group_call_rtmp_url_copied(tr::now));
|
|
}));
|
|
Settings::AddSkip(container, st::groupCallRtmpCopyButtonTopSkip);
|
|
const auto weak = container->add(std::move(wrap), rowPadding);
|
|
Settings::AddSkip(container, st::groupCallRtmpCopyButtonBottomSkip);
|
|
button->heightValue(
|
|
) | rpl::start_with_next([=](int height) {
|
|
weak->resize(weak->width(), height);
|
|
}, container->lifetime());
|
|
return weak;
|
|
};
|
|
|
|
const auto addLabel = [&](rpl::producer<QString> &&text) {
|
|
const auto label = container->add(
|
|
object_ptr<Ui::FlatLabel>(
|
|
container,
|
|
std::move(text),
|
|
*labelStyle,
|
|
*popupMenuStyle),
|
|
st::boxRowPadding + QMargins(0, 0, showButtonStyle->width, 0));
|
|
label->setSelectable(true);
|
|
label->setBreakEverywhere(true);
|
|
return label;
|
|
};
|
|
|
|
// Server URL.
|
|
Settings::AddSubsectionTitle(
|
|
container,
|
|
tr::lng_group_call_rtmp_url_subtitle(),
|
|
st::groupCallRtmpSubsectionTitleAddPadding,
|
|
subsectionTitleStyle);
|
|
|
|
auto urlLabelContent = state->url.value();
|
|
addLabel(std::move(urlLabelContent));
|
|
Settings::AddSkip(container, st::groupCallRtmpUrlSkip);
|
|
addButton(false, tr::lng_group_call_rtmp_url_copy());
|
|
//
|
|
|
|
if (divider) {
|
|
Settings::AddDivider(container);
|
|
}
|
|
|
|
// Stream Key.
|
|
Settings::AddSkip(container, st::groupCallRtmpKeySubsectionTitleSkip);
|
|
|
|
Settings::AddSubsectionTitle(
|
|
container,
|
|
tr::lng_group_call_rtmp_key_subtitle(),
|
|
st::groupCallRtmpSubsectionTitleAddPadding,
|
|
subsectionTitleStyle);
|
|
|
|
auto keyLabelContent = rpl::combine(
|
|
state->hidden.value(),
|
|
state->key.value()
|
|
) | rpl::map([passChar](bool hidden, const QString &key) {
|
|
return key.isEmpty()
|
|
? QString()
|
|
: hidden
|
|
? QString().fill(passChar, kPasswordCharAmount)
|
|
: key;
|
|
}) | rpl::after_next([=] {
|
|
container->resizeToWidth(container->widthNoMargins());
|
|
});
|
|
const auto streamKeyLabel = addLabel(std::move(keyLabelContent));
|
|
streamKeyLabel->setSelectable(false);
|
|
const auto streamKeyButton = Ui::CreateChild<Ui::IconButton>(
|
|
container.get(),
|
|
*showButtonStyle);
|
|
|
|
streamKeyLabel->topValue(
|
|
) | rpl::start_with_next([=, right = rowPadding.right()](int top) {
|
|
streamKeyButton->moveToRight(
|
|
st::groupCallRtmpShowButtonPosition.x(),
|
|
top + st::groupCallRtmpShowButtonPosition.y());
|
|
streamKeyButton->raise();
|
|
}, container->lifetime());
|
|
streamKeyButton->addClickHandler([=] {
|
|
const auto toggle = [=] {
|
|
const auto newValue = !state->hidden.current();
|
|
state->hidden = newValue;
|
|
streamKeyLabel->setSelectable(!newValue);
|
|
streamKeyLabel->setAttribute(
|
|
Qt::WA_TransparentForMouseEvents,
|
|
newValue);
|
|
};
|
|
if (!state->warned && state->hidden.current()) {
|
|
showBox(Ui::MakeConfirmBox({
|
|
.text = tr::lng_group_call_rtmp_key_warning(
|
|
Ui::Text::RichLangValue),
|
|
.confirmed = [=](Fn<void()> &&close) {
|
|
state->warned = true;
|
|
toggle();
|
|
close();
|
|
},
|
|
.confirmText = tr::lng_from_request_understand(),
|
|
.cancelText = tr::lng_close(),
|
|
.confirmStyle = attentionButtonStyle,
|
|
.labelStyle = labelStyle,
|
|
}));
|
|
} else {
|
|
toggle();
|
|
}
|
|
});
|
|
|
|
addButton(true, tr::lng_group_call_rtmp_key_copy());
|
|
//
|
|
}
|
|
|
|
} // namespace Calls::Group
|