| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*
- 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_channel.h"
- #include "data/data_chat.h"
- #include "data/data_user.h"
- #include "lang/lang_keys.h"
- #include "main/main_account.h"
- #include "main/main_session.h"
- #include "ui/boxes/confirm_box.h"
- #include "ui/layers/generic_box.h"
- #include "ui/text/text_utilities.h"
- #include "ui/toast/toast.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/popup_menu.h"
- #include "ui/wrap/vertical_layout.h"
- #include "ui/vertical_list.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 <QGuiApplication>
- #include <QStyle>
- namespace Calls::Group {
- namespace {
- constexpr auto kPasswordCharAmount = 24;
- void StartWithBox(
- not_null<Ui::GenericBox*> box,
- Fn<void()> done,
- Fn<void()> revoke,
- std::shared_ptr<Ui::Show> show,
- 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(show),
- std::move(data),
- &st::boxLabel,
- &st::groupCallRtmpShowButton,
- &st::defaultSubsectionTitle,
- &st::attentionBoxButton,
- &st::defaultPopupMenu);
- box->setTitle(tr::lng_group_call_rtmp_title());
- Ui::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() {
- close();
- }
- void StartRtmpProcess::start(
- not_null<PeerData*> peer,
- std::shared_ptr<Ui::Show> show,
- Fn<void(JoinInfo)> done) {
- Expects(done != nullptr);
- const auto session = &peer->session();
- if (_request) {
- if (_request->peer == peer) {
- _request->show = std::move(show);
- _request->done = std::move(done);
- return;
- }
- session->api().request(_request->id).cancel();
- _request = nullptr;
- }
- _request = std::make_unique<RtmpRequest>(
- RtmpRequest{
- .peer = peer,
- .show = std::move(show),
- .done = std::move(done),
- });
- session->account().sessionChanges(
- ) | rpl::start_with_next([=] {
- _request = nullptr;
- }, _request->lifetime);
- requestUrl(false);
- }
- void StartRtmpProcess::close() {
- if (_request) {
- _request->peer->session().api().request(_request->id).cancel();
- if (const auto strong = _request->box.data()) {
- strong->closeBox();
- }
- _request = nullptr;
- }
- }
- 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->show->showToast(Lang::Hard::ServerError());
- }).send();
- }
- void StartRtmpProcess::processUrl(RtmpInfo data) {
- if (!_request->box) {
- createBox();
- }
- _request->data = std::move(data);
- }
- void StartRtmpProcess::finish(JoinInfo info) {
- info.rtmpInfo = _request->data.current();
- _request->done(std::move(info));
- }
- void StartRtmpProcess::createBox() {
- auto done = [=] {
- const auto peer = _request->peer;
- const auto joinAs = (peer->isChat() && peer->asChat()->amCreator())
- ? peer
- : (peer->isChannel() && peer->asChannel()->amCreator())
- ? peer
- : peer->session().user();
- finish({ .peer = peer, .joinAs = joinAs, .rtmp = true });
- };
- auto revoke = [=] {
- const auto guard = base::make_weak(&_request->guard);
- _request->show->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->show,
- _request->data.value());
- object->boxClosing(
- ) | rpl::start_with_next([=] {
- _request = nullptr;
- }, _request->lifetime);
- _request->box = Ui::MakeWeak(object.data());
- _request->show->showBox(std::move(object));
- }
- void StartRtmpProcess::FillRtmpRows(
- not_null<Ui::VerticalLayout*> container,
- bool divider,
- std::shared_ptr<Ui::Show> show,
- 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 showToast = [=](const QString &text) {
- show->showToast(text);
- };
- 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));
- }));
- Ui::AddSkip(container, st::groupCallRtmpCopyButtonTopSkip);
- const auto weak = container->add(std::move(wrap), rowPadding);
- Ui::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.
- Ui::AddSubsectionTitle(
- container,
- tr::lng_group_call_rtmp_url_subtitle(),
- st::groupCallRtmpSubsectionTitleAddPadding,
- subsectionTitleStyle);
- auto urlLabelContent = state->url.value();
- addLabel(std::move(urlLabelContent));
- Ui::AddSkip(container, st::groupCallRtmpUrlSkip);
- addButton(false, tr::lng_group_call_rtmp_url_copy());
- //
- if (divider) {
- Ui::AddDivider(container);
- }
- // Stream Key.
- Ui::AddSkip(container, st::groupCallRtmpKeySubsectionTitleSkip);
- Ui::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()) {
- show->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_cancel(),
- .confirmStyle = attentionButtonStyle,
- .labelStyle = labelStyle,
- }));
- } else {
- toggle();
- }
- });
- addButton(true, tr::lng_group_call_rtmp_key_copy());
- //
- }
- } // namespace Calls::Group
|