| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844 |
- /*
- 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 "info/profile/info_profile_actions.h"
- #include "api/api_blocked_peers.h"
- #include "api/api_chat_participants.h"
- #include "api/api_credits.h"
- #include "api/api_statistics.h"
- #include "apiwrap.h"
- #include "base/options.h"
- #include "base/timer_rpl.h"
- #include "base/unixtime.h"
- #include "boxes/peers/add_bot_to_chat_box.h"
- #include "boxes/peers/edit_contact_box.h"
- #include "boxes/peers/edit_participants_box.h"
- #include "boxes/peers/edit_peer_info_box.h"
- #include "boxes/peers/verify_peers_box.h"
- #include "boxes/report_messages_box.h"
- #include "boxes/share_box.h"
- #include "boxes/star_gift_box.h"
- #include "boxes/translate_box.h"
- #include "core/application.h"
- #include "core/click_handler_types.h"
- #include "core/ui_integration.h"
- #include "data/business/data_business_common.h"
- #include "data/business/data_business_info.h"
- #include "data/components/credits.h"
- #include "data/data_changes.h"
- #include "data/data_channel.h"
- #include "data/data_chat.h"
- #include "data/data_folder.h"
- #include "data/data_forum_topic.h"
- #include "data/data_peer_values.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "data/notify/data_notify_settings.h"
- #include "data/stickers/data_custom_emoji.h"
- #include "dialogs/ui/dialogs_layout.h"
- #include "dialogs/ui/dialogs_message_view.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "history/history_item_components.h"
- #include "history/history_item_helpers.h"
- #include "history/view/history_view_item_preview.h"
- #include "info/bot/earn/info_bot_earn_widget.h"
- #include "info/bot/starref/info_bot_starref_common.h"
- #include "info/channel_statistics/earn/earn_format.h"
- #include "info/channel_statistics/earn/earn_icons.h"
- #include "info/channel_statistics/earn/info_channel_earn_list.h"
- #include "info/profile/info_profile_cover.h"
- #include "info/profile/info_profile_icon.h"
- #include "info/profile/info_profile_phone_menu.h"
- #include "info/profile/info_profile_text.h"
- #include "info/profile/info_profile_values.h"
- #include "info/profile/info_profile_widget.h"
- #include "info/info_controller.h"
- #include "info/info_memento.h"
- #include "inline_bots/bot_attach_web_view.h"
- #include "iv/iv_instance.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "menu/menu_mute.h"
- #include "settings/settings_common.h"
- #include "support/support_helper.h"
- #include "ui/boxes/peer_qr_box.h"
- #include "ui/boxes/report_box_graphics.h"
- #include "ui/controls/userpic_button.h"
- #include "ui/effects/toggle_arrow.h"
- #include "ui/painter.h"
- #include "ui/rect.h"
- #include "ui/ui_utility.h"
- #include "ui/text/format_values.h"
- #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
- #include "ui/text/text_variant.h"
- #include "ui/toast/toast.h"
- #include "ui/vertical_list.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/labels.h"
- #include "ui/widgets/popup_menu.h"
- #include "ui/widgets/shadow.h"
- #include "ui/wrap/padding_wrap.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/wrap/vertical_layout.h"
- #include "window/window_controller.h" // Window::Controller::show.
- #include "window/window_peer_menu.h"
- #include "window/window_session_controller.h"
- #include "styles/style_channel_earn.h" // st::channelEarnCurrencyCommonMargins
- #include "styles/style_info.h"
- #include "styles/style_layers.h"
- #include "styles/style_menu_icons.h"
- #include "styles/style_settings.h" // settingsButtonRightSkip.
- #include "styles/style_window.h" // mainMenuToggleFourStrokes.
- #include <QtGui/QGuiApplication>
- #include <QtGui/QClipboard>
- namespace Info {
- namespace Profile {
- namespace {
- constexpr auto kDay = Data::WorkingInterval::kDay;
- base::options::toggle ShowPeerIdBelowAbout({
- .id = kOptionShowPeerIdBelowAbout,
- .name = "Show Peer IDs in Profile",
- .description = "Show peer IDs from API below their Bio / Description."
- " Add contact IDs to exported data.",
- });
- [[nodiscard]] rpl::producer<TextWithEntities> UsernamesSubtext(
- not_null<PeerData*> peer,
- rpl::producer<QString> fallback) {
- return rpl::combine(
- UsernamesValue(peer),
- std::move(fallback)
- ) | rpl::map([](std::vector<TextWithEntities> usernames, QString text) {
- if (usernames.size() < 2) {
- return TextWithEntities{ .text = text };
- } else {
- auto result = TextWithEntities();
- result.append(tr::lng_info_usernames_label(tr::now));
- result.append(' ');
- auto &&subrange = ranges::make_subrange(
- begin(usernames) + 1,
- end(usernames));
- for (auto &username : std::move(subrange)) {
- const auto isLast = (usernames.back() == username);
- result.append(Ui::Text::Link(
- '@' + base::take(username.text),
- username.entities.front().data()));
- if (!isLast) {
- result.append(u", "_q);
- }
- }
- return result;
- }
- });
- }
- [[nodiscard]] Fn<void(QString)> UsernamesLinkCallback(
- not_null<PeerData*> peer,
- not_null<Window::SessionController*> controller,
- const QString &addToLink) {
- const auto weak = base::make_weak(controller);
- return [=](QString link) {
- if (link.startsWith(u"internal:"_q)) {
- Core::App().openInternalUrl(link,
- QVariant::fromValue(ClickHandlerContext{
- .sessionWindow = weak,
- }));
- return;
- } else if (!link.startsWith(u"https://"_q)) {
- link = peer->session().createInternalLinkFull(peer->username())
- + addToLink;
- }
- if (!link.isEmpty()) {
- TextUtilities::SetClipboardText({ link });
- if (const auto strong = weak.get()) {
- strong->showToast(
- tr::lng_channel_public_link_copied(tr::now));
- }
- }
- };
- }
- [[nodiscard]] object_ptr<Ui::RpWidget> CreateSkipWidget(
- not_null<Ui::RpWidget*> parent) {
- return Ui::CreateSkipWidget(parent, st::infoProfileSkip);
- }
- [[nodiscard]] object_ptr<Ui::SlideWrap<>> CreateSlideSkipWidget(
- not_null<Ui::RpWidget*> parent) {
- auto result = Ui::CreateSlideSkipWidget(
- parent,
- st::infoProfileSkip);
- result->setDuration(st::infoSlideDuration);
- return result;
- }
- [[nodiscard]] rpl::producer<TextWithEntities> AboutWithIdValue(
- not_null<PeerData*> peer) {
- return AboutValue(
- peer
- ) | rpl::map([=](TextWithEntities &&value) {
- if (!ShowPeerIdBelowAbout.value()) {
- return std::move(value);
- }
- using namespace Ui::Text;
- if (!value.empty()) {
- value.append("\n\n");
- }
- value.append(Italic(u"id: "_q));
- const auto raw = peer->id.value & PeerId::kChatTypeMask;
- value.append(Link(
- Italic(Lang::FormatCountDecimal(raw)),
- "internal:~peer_id~:copy:" + QString::number(raw)));
- return std::move(value);
- });
- }
- [[nodiscard]] bool AreNonTrivialHours(const Data::WorkingHours &hours) {
- if (!hours) {
- return false;
- }
- const auto &intervals = hours.intervals.list;
- for (auto i = 0; i != 7; ++i) {
- const auto day = Data::WorkingInterval{ i * kDay, (i + 1) * kDay };
- for (const auto &interval : intervals) {
- const auto intersection = interval.intersected(day);
- if (intersection && intersection != day) {
- return true;
- }
- }
- }
- return false;
- }
- [[nodiscard]] TimeId OpensIn(
- const Data::WorkingIntervals &intervals,
- TimeId now) {
- using namespace Data;
- while (now < 0) {
- now += WorkingInterval::kWeek;
- }
- while (now > WorkingInterval::kWeek) {
- now -= WorkingInterval::kWeek;
- }
- auto closest = WorkingInterval::kWeek;
- for (const auto &interval : intervals.list) {
- if (interval.start <= now && interval.end > now) {
- return TimeId(0);
- } else if (interval.start > now && interval.start - now < closest) {
- closest = interval.start - now;
- } else if (interval.start < now) {
- const auto next = interval.start + WorkingInterval::kWeek - now;
- if (next < closest) {
- closest = next;
- }
- }
- }
- return closest;
- }
- [[nodiscard]] rpl::producer<QString> OpensInText(
- rpl::producer<TimeId> in,
- rpl::producer<bool> hoursExpanded,
- rpl::producer<QString> fallback) {
- return rpl::combine(
- std::move(in),
- std::move(hoursExpanded),
- std::move(fallback)
- ) | rpl::map([](TimeId in, bool hoursExpanded, QString fallback) {
- return (!in || hoursExpanded)
- ? std::move(fallback)
- : (in >= 86400)
- ? tr::lng_info_hours_opens_in_days(tr::now, lt_count, in / 86400)
- : (in >= 3600)
- ? tr::lng_info_hours_opens_in_hours(tr::now, lt_count, in / 3600)
- : tr::lng_info_hours_opens_in_minutes(
- tr::now,
- lt_count,
- std::max(in / 60, 1));
- });
- }
- [[nodiscard]] QString FormatDayTime(TimeId time) {
- const auto wrap = [](TimeId value) {
- const auto hours = value / 3600;
- const auto minutes = (value % 3600) / 60;
- return QString::number(hours).rightJustified(2, u'0')
- + ':'
- + QString::number(minutes).rightJustified(2, u'0');
- };
- return (time > kDay)
- ? tr::lng_info_hours_next_day(tr::now, lt_time, wrap(time - kDay))
- : wrap(time == kDay ? 0 : time);
- }
- [[nodiscard]] QString JoinIntervals(const Data::WorkingIntervals &data) {
- auto result = QStringList();
- result.reserve(data.list.size());
- for (const auto &interval : data.list) {
- const auto start = FormatDayTime(interval.start);
- const auto end = FormatDayTime(interval.end);
- result.push_back(start + u" - "_q + end);
- }
- return result.join('\n');
- }
- [[nodiscard]] QString FormatDayHours(
- const Data::WorkingHours &hours,
- const Data::WorkingIntervals &mine,
- bool my,
- int day) {
- using namespace Data;
- const auto local = ExtractDayIntervals(hours.intervals, day);
- if (IsFullOpen(local)) {
- return tr::lng_info_hours_open_full(tr::now);
- }
- const auto use = my ? ExtractDayIntervals(mine, day) : local;
- if (!use) {
- return tr::lng_info_hours_closed(tr::now);
- }
- return JoinIntervals(use);
- }
- [[nodiscard]] Data::WorkingIntervals ShiftedIntervals(
- Data::WorkingIntervals intervals,
- int delta) {
- auto &list = intervals.list;
- if (!delta || list.empty()) {
- return { std::move(list) };
- }
- for (auto &interval : list) {
- interval.start += delta;
- interval.end += delta;
- }
- while (list.front().start < 0) {
- constexpr auto kWeek = Data::WorkingInterval::kWeek;
- const auto first = list.front();
- if (first.end > 0) {
- list.push_back({ first.start + kWeek, kWeek });
- list.front().start = 0;
- } else {
- list.push_back(first.shifted(kWeek));
- list.erase(list.begin());
- }
- }
- return intervals.normalized();
- }
- [[nodiscard]] object_ptr<Ui::SlideWrap<>> CreateWorkingHours(
- not_null<QWidget*> parent,
- not_null<UserData*> user) {
- using namespace Data;
- auto result = object_ptr<Ui::SlideWrap<Ui::RoundButton>>(
- parent,
- object_ptr<Ui::RoundButton>(
- parent,
- rpl::single(QString()),
- st::infoHoursOuter),
- st::infoProfileLabeledPadding - st::infoHoursOuterMargin);
- const auto button = result->entity();
- const auto inner = Ui::CreateChild<Ui::VerticalLayout>(button);
- button->widthValue() | rpl::start_with_next([=](int width) {
- const auto margin = st::infoHoursOuterMargin;
- inner->resizeToWidth(width - margin.left() - margin.right());
- inner->move(margin.left(), margin.top());
- }, inner->lifetime());
- inner->heightValue() | rpl::start_with_next([=](int height) {
- const auto margin = st::infoHoursOuterMargin;
- height += margin.top() + margin.bottom();
- button->resize(button->width(), height);
- }, inner->lifetime());
- const auto info = &user->owner().businessInfo();
- struct State {
- rpl::variable<WorkingHours> hours;
- rpl::variable<TimeId> time;
- rpl::variable<int> day;
- rpl::variable<int> timezoneDelta;
- rpl::variable<WorkingIntervals> mine;
- rpl::variable<WorkingIntervals> mineByDays;
- rpl::variable<TimeId> opensIn;
- rpl::variable<bool> opened;
- rpl::variable<bool> expanded;
- rpl::variable<bool> nonTrivial;
- rpl::variable<bool> myTimezone;
- rpl::event_stream<> recounts;
- };
- const auto state = inner->lifetime().make_state<State>();
- auto recounts = state->recounts.events_starting_with_copy(rpl::empty);
- const auto recount = [=] {
- state->recounts.fire({});
- };
- state->hours = user->session().changes().peerFlagsValue(
- user,
- PeerUpdate::Flag::BusinessDetails
- ) | rpl::map([=] {
- return user->businessDetails().hours;
- });
- state->nonTrivial = state->hours.value() | rpl::map(AreNonTrivialHours);
- const auto seconds = QTime::currentTime().msecsSinceStartOfDay() / 1000;
- const auto inMinute = seconds % 60;
- const auto firstTick = inMinute ? (61 - inMinute) : 1;
- state->time = rpl::single(rpl::empty) | rpl::then(
- base::timer_once(firstTick * crl::time(1000))
- ) | rpl::then(
- base::timer_each(60 * crl::time(1000))
- ) | rpl::map([] {
- const auto local = QDateTime::currentDateTime();
- const auto day = local.date().dayOfWeek() - 1;
- const auto seconds = local.time().msecsSinceStartOfDay() / 1000;
- return day * kDay + seconds;
- });
- state->day = state->time.value() | rpl::map([](TimeId time) {
- return time / kDay;
- });
- state->timezoneDelta = rpl::combine(
- state->hours.value(),
- info->timezonesValue()
- ) | rpl::filter([](
- const WorkingHours &hours,
- const Timezones &timezones) {
- return ranges::contains(
- timezones.list,
- hours.timezoneId,
- &Timezone::id);
- }) | rpl::map([](WorkingHours &&hours, const Timezones &timezones) {
- const auto &list = timezones.list;
- const auto closest = FindClosestTimezoneId(list);
- const auto i = ranges::find(list, closest, &Timezone::id);
- const auto j = ranges::find(list, hours.timezoneId, &Timezone::id);
- Assert(i != end(list));
- Assert(j != end(list));
- return i->utcOffset - j->utcOffset;
- });
- state->mine = rpl::combine(
- state->hours.value(),
- state->timezoneDelta.value()
- ) | rpl::map([](WorkingHours &&hours, int delta) {
- return ShiftedIntervals(hours.intervals, delta);
- });
- state->opensIn = rpl::combine(
- state->mine.value(),
- state->time.value()
- ) | rpl::map([](const WorkingIntervals &mine, TimeId time) {
- return OpensIn(mine, time);
- });
- state->opened = state->opensIn.value() | rpl::map(rpl::mappers::_1 == 0);
- state->mineByDays = rpl::combine(
- state->hours.value(),
- state->timezoneDelta.value()
- ) | rpl::map([](WorkingHours &&hours, int delta) {
- auto full = std::array<bool, 7>();
- auto withoutFullDays = hours.intervals;
- for (auto i = 0; i != 7; ++i) {
- if (IsFullOpen(ExtractDayIntervals(hours.intervals, i))) {
- full[i] = true;
- withoutFullDays = ReplaceDayIntervals(
- withoutFullDays,
- i,
- Data::WorkingIntervals());
- }
- }
- auto result = ShiftedIntervals(withoutFullDays, delta);
- for (auto i = 0; i != 7; ++i) {
- if (full[i]) {
- result = ReplaceDayIntervals(
- result,
- i,
- Data::WorkingIntervals{ { { 0, kDay } } });
- }
- }
- return result;
- });
- const auto dayHoursText = [=](int day) {
- return rpl::combine(
- state->hours.value(),
- state->mineByDays.value(),
- state->myTimezone.value()
- ) | rpl::map([=](
- const WorkingHours &hours,
- const WorkingIntervals &mine,
- bool my) {
- return FormatDayHours(hours, mine, my, day);
- });
- };
- const auto dayHoursTextValue = [=](rpl::producer<int> day) {
- return std::move(day)
- | rpl::map(dayHoursText)
- | rpl::flatten_latest();
- };
- const auto openedWrap = inner->add(object_ptr<Ui::RpWidget>(inner));
- const auto opened = Ui::CreateChild<Ui::FlatLabel>(
- openedWrap,
- rpl::conditional(
- state->opened.value(),
- tr::lng_info_work_open(),
- tr::lng_info_work_closed()
- ) | rpl::after_next(recount),
- st::infoHoursState);
- opened->setAttribute(Qt::WA_TransparentForMouseEvents);
- const auto timing = Ui::CreateChild<Ui::FlatLabel>(
- openedWrap,
- OpensInText(
- state->opensIn.value(),
- state->expanded.value(),
- dayHoursTextValue(state->day.value())
- ) | rpl::after_next(recount),
- st::infoHoursValue);
- const auto timingArrow = Ui::CreateChild<Ui::RpWidget>(openedWrap);
- timingArrow->resize(Size(timing->st().style.font->height));
- timing->setAttribute(Qt::WA_TransparentForMouseEvents);
- state->opened.value() | rpl::start_with_next([=](bool value) {
- opened->setTextColorOverride(value
- ? st::boxTextFgGood->c
- : st::boxTextFgError->c);
- }, opened->lifetime());
- rpl::combine(
- openedWrap->widthValue(),
- opened->heightValue(),
- timing->sizeValue()
- ) | rpl::start_with_next([=](int width, int h1, QSize size) {
- opened->moveToLeft(0, 0, width);
- timingArrow->moveToRight(0, 0, width);
- timing->moveToRight(timingArrow->width(), 0, width);
- const auto margins = opened->getMargins();
- const auto added = margins.top() + margins.bottom();
- openedWrap->resize(width, std::max(h1, size.height()) - added);
- }, openedWrap->lifetime());
- const auto labelWrap = inner->add(object_ptr<Ui::RpWidget>(inner));
- const auto label = Ui::CreateChild<Ui::FlatLabel>(
- labelWrap,
- tr::lng_info_hours_label(),
- st::infoLabel);
- label->setAttribute(Qt::WA_TransparentForMouseEvents);
- auto linkText = rpl::combine(
- state->nonTrivial.value(),
- state->hours.value(),
- state->mine.value(),
- state->myTimezone.value()
- ) | rpl::map([=](
- bool complex,
- const WorkingHours &hours,
- const WorkingIntervals &mine,
- bool my) {
- return (!complex || hours.intervals == mine)
- ? rpl::single(QString())
- : my
- ? tr::lng_info_hours_my_time()
- : tr::lng_info_hours_local_time();
- }) | rpl::flatten_latest();
- const auto link = Ui::CreateChild<Ui::RoundButton>(
- labelWrap,
- std::move(linkText),
- st::defaultTableSmallButton);
- link->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
- link->setClickedCallback([=] {
- state->myTimezone = !state->myTimezone.current();
- state->expanded = true;
- });
- rpl::combine(
- labelWrap->widthValue(),
- label->heightValue(),
- link->sizeValue()
- ) | rpl::start_with_next([=](int width, int h1, QSize size) {
- label->moveToLeft(0, 0, width);
- link->moveToRight(0, 0, width);
- const auto margins = label->getMargins();
- const auto added = margins.top() + margins.bottom();
- labelWrap->resize(width, std::max(h1, size.height()) - added);
- }, labelWrap->lifetime());
- const auto other = inner->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- inner,
- object_ptr<Ui::VerticalLayout>(inner)));
- other->toggleOn(state->expanded.value(), anim::type::normal);
- constexpr auto kSlideDuration = float64(st::slideWrapDuration);
- other->setDuration(kSlideDuration);
- {
- const auto arrowAnimation
- = other->lifetime().make_state<Ui::Animations::Basic>();
- arrowAnimation->init([=] {
- timingArrow->update();
- if (!other->animating()) {
- arrowAnimation->stop();
- }
- });
- timingArrow->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(timingArrow);
- const auto progress = other->animating()
- ? (crl::now() - arrowAnimation->started()) / kSlideDuration
- : 1.;
- const auto path = Ui::ToggleUpDownArrowPath(
- timingArrow->width() / 2,
- timingArrow->height() / 2,
- st::infoHoursArrowSize,
- st::mainMenuToggleFourStrokes,
- other->toggled() ? progress : 1 - progress);
- auto hq = PainterHighQualityEnabler(p);
- p.fillPath(path, timing->st().textFg);
- }, timingArrow->lifetime());
- state->expanded.value() | rpl::start_with_next([=] {
- arrowAnimation->start();
- }, other->lifetime());
- }
- other->finishAnimating();
- const auto days = other->entity();
- for (auto i = 1; i != 7; ++i) {
- const auto dayWrap = days->add(
- object_ptr<Ui::RpWidget>(other),
- QMargins(0, st::infoHoursDaySkip, 0, 0));
- auto label = state->day.value() | rpl::map([=](int day) {
- switch ((day + i) % 7) {
- case 0: return tr::lng_hours_monday();
- case 1: return tr::lng_hours_tuesday();
- case 2: return tr::lng_hours_wednesday();
- case 3: return tr::lng_hours_thursday();
- case 4: return tr::lng_hours_friday();
- case 5: return tr::lng_hours_saturday();
- case 6: return tr::lng_hours_sunday();
- }
- Unexpected("Index in working hours.");
- }) | rpl::flatten_latest();
- const auto dayLabel = Ui::CreateChild<Ui::FlatLabel>(
- dayWrap,
- std::move(label),
- st::infoHoursDayLabel);
- dayLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
- const auto dayHours = Ui::CreateChild<Ui::FlatLabel>(
- dayWrap,
- dayHoursTextValue(state->day.value()
- | rpl::map((rpl::mappers::_1 + i) % 7)),
- st::infoHoursValue);
- dayHours->setAttribute(Qt::WA_TransparentForMouseEvents);
- rpl::combine(
- dayWrap->widthValue(),
- dayLabel->heightValue(),
- dayHours->sizeValue()
- ) | rpl::start_with_next([=](int width, int h1, QSize size) {
- dayLabel->moveToLeft(0, 0, width);
- dayHours->moveToRight(0, 0, width);
- const auto margins = dayLabel->getMargins();
- const auto added = margins.top() + margins.bottom();
- dayWrap->resize(width, std::max(h1, size.height()) - added);
- }, dayWrap->lifetime());
- }
- button->setClickedCallback([=] {
- state->expanded = !state->expanded.current();
- });
- result->toggleOn(state->hours.value(
- ) | rpl::map([](const WorkingHours &data) {
- return bool(data);
- }));
- return result;
- }
- [[nodiscard]] object_ptr<Ui::SlideWrap<>> CreateBirthday(
- not_null<QWidget*> parent,
- not_null<Window::SessionController*> controller,
- not_null<UserData*> user) {
- using namespace Data;
- auto result = object_ptr<Ui::SlideWrap<Ui::RoundButton>>(
- parent,
- object_ptr<Ui::RoundButton>(
- parent,
- rpl::single(QString()),
- st::infoHoursOuter),
- st::infoProfileLabeledPadding - st::infoHoursOuterMargin);
- result->setDuration(st::infoSlideDuration);
- const auto button = result->entity();
- auto outer = Ui::CreateChild<Ui::SlideWrap<Ui::VerticalLayout>>(
- button,
- object_ptr<Ui::VerticalLayout>(button),
- st::infoHoursOuterMargin);
- const auto layout = outer->entity();
- layout->setAttribute(Qt::WA_TransparentForMouseEvents);
- auto birthday = BirthdayValue(
- user
- ) | rpl::start_spawning(result->lifetime());
- auto label = BirthdayLabelText(rpl::duplicate(birthday));
- auto text = BirthdayValueText(
- rpl::duplicate(birthday)
- ) | Ui::Text::ToWithEntities();
- const auto giftIcon = Ui::CreateChild<Ui::RpWidget>(layout);
- giftIcon->resize(st::birthdayTodayIcon.size());
- layout->sizeValue() | rpl::start_with_next([=](QSize size) {
- giftIcon->moveToRight(
- 0,
- (size.height() - giftIcon->height()) / 2,
- size.width());
- }, giftIcon->lifetime());
- giftIcon->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(giftIcon);
- st::birthdayTodayIcon.paint(p, 0, 0, giftIcon->width());
- }, giftIcon->lifetime());
- rpl::duplicate(
- birthday
- ) | rpl::map([](Data::Birthday value) {
- return Data::IsBirthdayTodayValue(value);
- }) | rpl::flatten_latest(
- ) | rpl::distinct_until_changed(
- ) | rpl::start_with_next([=](bool today) {
- const auto disable = !today && user->session().premiumCanBuy();
- button->setDisabled(disable);
- button->setAttribute(Qt::WA_TransparentForMouseEvents, disable);
- button->clearState();
- giftIcon->setVisible(!disable);
- }, result->lifetime());
- auto nonEmptyText = std::move(
- text
- ) | rpl::before_next([slide = result.data()](
- const TextWithEntities &value) {
- if (value.text.isEmpty()) {
- slide->hide(anim::type::normal);
- }
- }) | rpl::filter([](const TextWithEntities &value) {
- return !value.text.isEmpty();
- }) | rpl::after_next([slide = result.data()](
- const TextWithEntities &value) {
- slide->show(anim::type::normal);
- });
- layout->add(object_ptr<Ui::FlatLabel>(
- layout,
- std::move(nonEmptyText),
- st::birthdayLabeled));
- layout->add(Ui::CreateSkipWidget(layout, st::infoLabelSkip));
- layout->add(object_ptr<Ui::FlatLabel>(
- layout,
- std::move(
- label
- ) | rpl::after_next([=] {
- layout->resizeToWidth(layout->widthNoMargins());
- }),
- st::birthdayLabel));
- result->finishAnimating();
- Ui::ResizeFitChild(button, outer);
- button->setClickedCallback([=] {
- if (!button->isDisabled()) {
- Ui::ShowStarGiftBox(controller, user);
- }
- });
- return result;
- }
- template <typename Text, typename ToggleOn, typename Callback>
- auto AddActionButton(
- not_null<Ui::VerticalLayout*> parent,
- Text &&text,
- ToggleOn &&toggleOn,
- Callback &&callback,
- const style::icon *icon,
- const style::SettingsButton &st = st::infoSharedMediaButton) {
- auto result = parent->add(object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
- parent,
- object_ptr<Ui::SettingsButton>(
- parent,
- std::move(text),
- st))
- );
- result->setDuration(
- st::infoSlideDuration
- )->toggleOn(
- std::move(toggleOn)
- )->entity()->addClickHandler(std::move(callback));
- result->finishAnimating();
- if (icon) {
- object_ptr<Profile::FloatingIcon>(
- result,
- *icon,
- st::infoSharedMediaButtonIconPosition);
- }
- return result;
- };
- template <typename Text, typename ToggleOn, typename Callback>
- [[nodiscard]] auto AddMainButton(
- not_null<Ui::VerticalLayout*> parent,
- Text &&text,
- ToggleOn &&toggleOn,
- Callback &&callback,
- Ui::MultiSlideTracker &tracker,
- const style::SettingsButton &st = st::infoMainButton) {
- tracker.track(AddActionButton(
- parent,
- std::move(text) | Ui::Text::ToUpper(),
- std::move(toggleOn),
- std::move(callback),
- nullptr,
- st));
- }
- rpl::producer<uint64> AddCurrencyAction(
- not_null<UserData*> user,
- not_null<Ui::VerticalLayout*> wrap,
- not_null<Controller*> controller) {
- struct State final {
- rpl::variable<uint64> balance;
- };
- const auto state = wrap->lifetime().make_state<State>();
- const auto parentController = controller->parentController();
- const auto wrapButton = AddActionButton(
- wrap,
- tr::lng_manage_peer_bot_balance_currency(),
- state->balance.value() | rpl::map(rpl::mappers::_1 > 0),
- [=] { parentController->showSection(Info::ChannelEarn::Make(user)); },
- nullptr);
- {
- const auto button = wrapButton->entity();
- const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
- icon->resize(st::infoIconReport.size());
- const auto image = Ui::Earn::MenuIconCurrency(icon->size());
- icon->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(icon);
- p.drawImage(0, 0, image);
- }, icon->lifetime());
- button->sizeValue(
- ) | rpl::start_with_next([=](const QSize &size) {
- icon->move(st::infoEarnCurrencyIconPosition);
- }, icon->lifetime());
- }
- const auto balance = user->session().credits().balanceCurrency(user->id);
- if (balance) {
- state->balance = balance;
- }
- {
- const auto weak = Ui::MakeWeak(wrap);
- const auto currencyLoadLifetime
- = std::make_shared<rpl::lifetime>();
- const auto currencyLoad
- = currencyLoadLifetime->make_state<Api::EarnStatistics>(user);
- currencyLoad->request(
- ) | rpl::start_with_error_done([=](const QString &error) {
- currencyLoadLifetime->destroy();
- }, [=] {
- if (const auto strong = weak.data()) {
- state->balance = currencyLoad->data().currentBalance;
- currencyLoadLifetime->destroy();
- }
- }, *currencyLoadLifetime);
- }
- const auto &st = st::infoSharedMediaButton;
- const auto button = wrapButton->entity();
- const auto name = Ui::CreateChild<Ui::FlatLabel>(button, st.rightLabel);
- const auto icon = Ui::Text::SingleCustomEmoji(
- user->owner().customEmojiManager().registerInternalEmoji(
- Ui::Earn::IconCurrencyColored(
- st.rightLabel.style.font,
- st.rightLabel.textFg->c),
- st::channelEarnCurrencyCommonMargins,
- false));
- name->show();
- rpl::combine(
- button->widthValue(),
- tr::lng_manage_peer_bot_balance_currency(),
- state->balance.value()
- ) | rpl::start_with_next([=, &st](
- int width,
- const QString &button,
- uint64 balance) {
- const auto available = width
- - rect::m::sum::h(st.padding)
- - st.style.font->width(button)
- - st::settingsButtonRightSkip;
- name->setMarkedText(
- base::duplicate(icon)
- .append(QChar(' '))
- .append(Info::ChannelEarn::MajorPart(balance))
- .append(Info::ChannelEarn::MinorPart(balance)),
- Core::TextContext({
- .session = &user->session(),
- .repaint = [=] { name->update(); },
- }));
- name->resizeToNaturalWidth(available);
- name->moveToRight(st::settingsButtonRightSkip, st.padding.top());
- }, name->lifetime());
- name->setAttribute(Qt::WA_TransparentForMouseEvents);
- wrapButton->finishAnimating();
- return state->balance.value();
- }
- rpl::producer<StarsAmount> AddCreditsAction(
- not_null<UserData*> user,
- not_null<Ui::VerticalLayout*> wrap,
- not_null<Controller*> controller) {
- struct State final {
- rpl::variable<StarsAmount> balance;
- };
- const auto state = wrap->lifetime().make_state<State>();
- const auto parentController = controller->parentController();
- const auto wrapButton = AddActionButton(
- wrap,
- tr::lng_manage_peer_bot_balance_credits(),
- state->balance.value() | rpl::map(rpl::mappers::_1 > StarsAmount(0)),
- [=] { parentController->showSection(Info::BotEarn::Make(user)); },
- nullptr);
- {
- const auto button = wrapButton->entity();
- const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
- const auto image = Ui::Earn::MenuIconCredits();
- icon->resize(image.size() / style::DevicePixelRatio());
- icon->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(icon);
- p.drawImage(0, 0, image);
- }, icon->lifetime());
- button->sizeValue(
- ) | rpl::start_with_next([=](const QSize &size) {
- icon->move(st::infoEarnCreditsIconPosition);
- }, icon->lifetime());
- }
- if (const auto balance = user->session().credits().balance(user->id)) {
- state->balance = balance;
- }
- {
- const auto api = wrap->lifetime().make_state<Api::CreditsStatus>(
- user);
- api->request({}, [=](Data::CreditsStatusSlice data) {
- state->balance = data.balance;
- });
- }
- const auto &st = st::infoSharedMediaButton;
- const auto button = wrapButton->entity();
- const auto name = Ui::CreateChild<Ui::FlatLabel>(button, st.rightLabel);
- const auto icon = user->owner().customEmojiManager().creditsEmoji();
- name->show();
- rpl::combine(
- button->widthValue(),
- tr::lng_manage_peer_bot_balance_credits(),
- state->balance.value()
- ) | rpl::start_with_next([=, &st](
- int width,
- const QString &button,
- StarsAmount balance) {
- const auto available = width
- - rect::m::sum::h(st.padding)
- - st.style.font->width(button)
- - st::settingsButtonRightSkip;
- name->setMarkedText(
- base::duplicate(icon)
- .append(QChar(' '))
- .append(Lang::FormatStarsAmountDecimal(balance)),
- Core::TextContext({
- .session = &user->session(),
- .repaint = [=] { name->update(); },
- }));
- name->resizeToNaturalWidth(available);
- name->moveToRight(st::settingsButtonRightSkip, st.padding.top());
- }, name->lifetime());
- name->setAttribute(Qt::WA_TransparentForMouseEvents);
- wrapButton->finishAnimating();
- return state->balance.value();
- }
- class DetailsFiller {
- public:
- DetailsFiller(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer,
- Origin origin);
- DetailsFiller(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<Data::ForumTopic*> topic);
- object_ptr<Ui::RpWidget> fill();
- private:
- object_ptr<Ui::RpWidget> setupPersonalChannel(not_null<UserData*> user);
- object_ptr<Ui::RpWidget> setupInfo();
- object_ptr<Ui::RpWidget> setupMuteToggle();
- void setupAboutVerification();
- void setupMainApp();
- void setupBotPermissions();
- void setupMainButtons();
- Ui::MultiSlideTracker fillTopicButtons();
- Ui::MultiSlideTracker fillUserButtons(
- not_null<UserData*> user);
- Ui::MultiSlideTracker fillChannelButtons(
- not_null<ChannelData*> channel);
- Ui::MultiSlideTracker fillDiscussionButtons(
- not_null<ChannelData*> channel);
- void addReportReaction(Ui::MultiSlideTracker &tracker);
- void addReportReaction(
- GroupReactionOrigin data,
- bool ban,
- Ui::MultiSlideTracker &tracker);
- template <
- typename Widget,
- typename = std::enable_if_t<
- std::is_base_of_v<Ui::RpWidget, Widget>>>
- Widget *add(
- object_ptr<Widget> &&child,
- const style::margins &margin = style::margins()) {
- return _wrap->add(
- std::move(child),
- margin);
- }
- not_null<Controller*> _controller;
- not_null<Ui::RpWidget*> _parent;
- not_null<PeerData*> _peer;
- Data::ForumTopic *_topic = nullptr;
- Origin _origin;
- object_ptr<Ui::VerticalLayout> _wrap;
- };
- class ActionsFiller {
- public:
- ActionsFiller(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer);
- object_ptr<Ui::RpWidget> fill();
- private:
- void addAffiliateProgram(not_null<UserData*> user);
- void addBalanceActions(not_null<UserData*> user);
- void addInviteToGroupAction(not_null<UserData*> user);
- void addShareContactAction(not_null<UserData*> user);
- void addEditContactAction(not_null<UserData*> user);
- void addDeleteContactAction(not_null<UserData*> user);
- void addBotCommandActions(not_null<UserData*> user);
- void addFastButtonsMode(not_null<UserData*> user);
- void addReportAction();
- void addBlockAction(not_null<UserData*> user);
- void addLeaveChannelAction(not_null<ChannelData*> channel);
- void addJoinChannelAction(not_null<ChannelData*> channel);
- void fillUserActions(not_null<UserData*> user);
- void fillChannelActions(not_null<ChannelData*> channel);
- not_null<Controller*> _controller;
- not_null<Ui::RpWidget*> _parent;
- not_null<PeerData*> _peer;
- object_ptr<Ui::VerticalLayout> _wrap = { nullptr };
- };
- void ReportReactionBox(
- not_null<Ui::GenericBox*> box,
- not_null<Window::SessionController*> controller,
- not_null<PeerData*> participant,
- GroupReactionOrigin data,
- bool ban,
- Fn<void()> sent) {
- box->setTitle(tr::lng_report_reaction_title());
- box->addRow(object_ptr<Ui::FlatLabel>(
- box,
- tr::lng_report_reaction_about(),
- st::boxLabel));
- const auto check = ban
- ? box->addRow(
- object_ptr<Ui::Checkbox>(
- box,
- tr::lng_report_and_ban_button(tr::now),
- true),
- st::boxRowPadding + QMargins{ 0, st::boxLittleSkip, 0, 0 })
- : nullptr;
- box->addButton(tr::lng_report_button(), [=] {
- const auto chat = data.group->asChat();
- const auto channel = data.group->asMegagroup();
- if (check && check->checked()) {
- if (chat) {
- chat->session().api().chatParticipants().kick(
- chat,
- participant);
- } else if (channel) {
- channel->session().api().chatParticipants().kick(
- channel,
- participant,
- ChatRestrictionsInfo());
- }
- }
- data.group->session().api().request(MTPmessages_ReportReaction(
- data.group->input,
- MTP_int(data.messageId.bare),
- participant->input
- )).done(crl::guard(controller, [=] {
- controller->showToast(tr::lng_report_thanks(tr::now));
- })).send();
- sent();
- box->closeBox();
- }, st::attentionBoxButton);
- box->addButton(tr::lng_cancel(), [=] {
- box->closeBox();
- });
- }
- DetailsFiller::DetailsFiller(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer,
- Origin origin)
- : _controller(controller)
- , _parent(parent)
- , _peer(peer)
- , _origin(origin)
- , _wrap(_parent) {
- }
- DetailsFiller::DetailsFiller(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<Data::ForumTopic*> topic)
- : _controller(controller)
- , _parent(parent)
- , _peer(topic->peer())
- , _topic(topic)
- , _wrap(_parent) {
- }
- template <typename T>
- bool SetClickContext(
- const ClickHandlerPtr &handler,
- const ClickContext &context) {
- if (const auto casted = std::dynamic_pointer_cast<T>(handler)) {
- casted->T::onClick(context);
- return true;
- }
- return false;
- }
- object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
- auto result = object_ptr<Ui::VerticalLayout>(_wrap);
- auto tracker = Ui::MultiSlideTracker();
- // Fill context for a mention / hashtag / bot command link.
- const auto infoClickFilter = [=,
- peer = _peer.get(),
- window = _controller->parentController()](
- const ClickHandlerPtr &handler,
- Qt::MouseButton button) {
- const auto context = ClickContext{
- button,
- QVariant::fromValue(ClickHandlerContext{
- .sessionWindow = base::make_weak(window),
- .peer = peer,
- })
- };
- if (SetClickContext<BotCommandClickHandler>(handler, context)) {
- return false;
- } else if (SetClickContext<MentionClickHandler>(handler, context)) {
- return false;
- } else if (SetClickContext<HashtagClickHandler>(handler, context)) {
- return false;
- } else if (SetClickContext<CashtagClickHandler>(handler, context)) {
- return false;
- } else if (SetClickContext<UrlClickHandler>(handler, context)) {
- return false;
- }
- return true;
- };
- const auto addTranslateToMenu = [&,
- peer = _peer.get(),
- controller = _controller->parentController()](
- not_null<Ui::FlatLabel*> label,
- rpl::producer<TextWithEntities> &&text) {
- struct State {
- rpl::variable<TextWithEntities> labelText;
- };
- const auto state = label->lifetime().make_state<State>();
- state->labelText = std::move(text);
- label->setContextMenuHook([=](
- Ui::FlatLabel::ContextMenuRequest request) {
- if (request.link) {
- const auto &url = request.link->url();
- if (url.startsWith(u"internal:~peer_id~:"_q)) {
- const auto weak = base::make_weak(controller);
- request.menu->addAction(u"Copy ID"_q, [=] {
- Core::App().openInternalUrl(
- url,
- QVariant::fromValue(ClickHandlerContext{
- .sessionWindow = weak,
- }));
- });
- return;
- }
- }
- label->fillContextMenu(request);
- if (Ui::SkipTranslate(state->labelText.current())) {
- return;
- }
- auto item = (request.selection.empty()
- ? tr::lng_context_translate
- : tr::lng_context_translate_selected)(tr::now);
- request.menu->addAction(std::move(item), [=] {
- controller->window().show(Box(
- Ui::TranslateBox,
- peer,
- MsgId(),
- request.selection.empty()
- ? state->labelText.current()
- : Ui::Text::Mid(
- state->labelText.current(),
- request.selection.from,
- request.selection.to - request.selection.from),
- false));
- });
- });
- };
- const auto addInfoLineGeneric = [&](
- v::text::data &&label,
- rpl::producer<TextWithEntities> &&text,
- const style::FlatLabel &textSt = st::infoLabeled,
- const style::margins &padding = st::infoProfileLabeledPadding) {
- auto line = CreateTextWithLabel(
- result,
- v::text::take_marked(std::move(label)),
- std::move(text),
- st::infoLabel,
- textSt,
- padding);
- tracker.track(result->add(std::move(line.wrap)));
- line.text->setClickHandlerFilter(infoClickFilter);
- return line;
- };
- const auto addInfoLine = [&](
- v::text::data &&label,
- rpl::producer<TextWithEntities> &&text,
- const style::FlatLabel &textSt = st::infoLabeled,
- const style::margins &padding = st::infoProfileLabeledPadding) {
- return addInfoLineGeneric(
- std::move(label),
- std::move(text),
- textSt,
- padding);
- };
- const auto addInfoOneLine = [&](
- v::text::data &&label,
- rpl::producer<TextWithEntities> &&text,
- const QString &contextCopyText,
- const style::margins &padding = st::infoProfileLabeledPadding) {
- auto result = addInfoLine(
- std::move(label),
- std::move(text),
- st::infoLabeledOneLine,
- padding);
- result.text->setDoubleClickSelectsParagraph(true);
- result.text->setContextCopyText(contextCopyText);
- return result;
- };
- const auto fitLabelToButton = [&](
- not_null<Ui::RpWidget*> button,
- not_null<Ui::FlatLabel*> label,
- int rightSkip) {
- const auto parent = label->parentWidget();
- const auto container = result.data();
- rpl::combine(
- container->widthValue(),
- label->geometryValue(),
- button->sizeValue()
- ) | rpl::start_with_next([=](int width, QRect, QSize buttonSize) {
- button->moveToRight(
- rightSkip,
- (parent->height() - buttonSize.height()) / 2);
- const auto x = Ui::MapFrom(container, label, QPoint(0, 0)).x();
- const auto s = Ui::MapFrom(container, button, QPoint(0, 0)).x();
- label->resizeToWidth(s - x);
- }, button->lifetime());
- };
- const auto controller = _controller->parentController();
- const auto weak = base::make_weak(controller);
- const auto peerIdRaw = QString::number(_peer->id.value);
- const auto lnkHook = [=](Ui::FlatLabel::ContextMenuRequest request) {
- const auto strong = weak.get();
- if (!strong || !request.link) {
- return;
- }
- const auto url = request.link->url();
- if (url.startsWith(u"https://")) {
- request.menu->addAction(
- tr::lng_context_copy_link(tr::now),
- [=] {
- TextUtilities::SetClipboardText({ url });
- if (const auto strong = weak.get()) {
- strong->showToast(
- tr::lng_channel_public_link_copied(tr::now));
- }
- });
- request.menu->addAction(
- tr::lng_group_invite_share(tr::now),
- [=] {
- if (const auto strong = weak.get()) {
- FastShareLink(strong, url);
- }
- });
- return;
- }
- static const auto kPrefix = QRegularExpression(u"^internal:"
- "(collectible_username|username_link|username_regular)/"
- "([a-zA-Z0-9\\-\\_\\.]+)@"_q);
- const auto match = kPrefix.match(url);
- if (!match.hasMatch()) {
- return;
- }
- const auto username = match.captured(2);
- const auto fullname = username + '@' + peerIdRaw;
- const auto mentionLink = "internal:username_regular/" + fullname;
- const auto linkLink = "internal:username_link/" + fullname;
- const auto context = QVariant::fromValue(ClickHandlerContext{
- .sessionWindow = weak,
- });
- const auto session = &strong->session();
- const auto link = session->createInternalLinkFull(username);
- request.menu->addAction(
- tr::lng_context_copy_mention(tr::now),
- [=] { Core::App().openInternalUrl(mentionLink, context); });
- request.menu->addAction(
- tr::lng_context_copy_link(tr::now),
- [=] { Core::App().openInternalUrl(linkLink, context); });
- request.menu->addAction(
- tr::lng_group_invite_share(tr::now),
- [=] {
- if (const auto strong = weak.get()) {
- FastShareLink(strong, link);
- }
- });
- };
- if (const auto user = _peer->asUser()) {
- if (user->session().supportMode()) {
- addInfoLineGeneric(
- user->session().supportHelper().infoLabelValue(user),
- user->session().supportHelper().infoTextValue(user));
- }
- {
- const auto phoneLabel = addInfoOneLine(
- tr::lng_info_mobile_label(),
- PhoneOrHiddenValue(user),
- tr::lng_profile_copy_phone(tr::now)).text;
- const auto hook = [=](Ui::FlatLabel::ContextMenuRequest request) {
- if (request.selection.empty()) {
- const auto callback = [=] {
- auto phone = rpl::variable<TextWithEntities>(
- PhoneOrHiddenValue(user)).current().text;
- phone.replace(' ', QString()).replace('-', QString());
- TextUtilities::SetClipboardText({ phone });
- };
- request.menu->addAction(
- tr::lng_profile_copy_phone(tr::now),
- callback);
- } else {
- phoneLabel->fillContextMenu(request);
- }
- AddPhoneMenu(request.menu, user);
- };
- phoneLabel->setContextMenuHook(hook);
- }
- auto label = user->isBot()
- ? tr::lng_info_about_label()
- : tr::lng_info_bio_label();
- addTranslateToMenu(
- addInfoLine(std::move(label), AboutWithIdValue(user)).text,
- AboutWithIdValue(user));
- const auto usernameLine = addInfoOneLine(
- UsernamesSubtext(_peer, tr::lng_info_username_label()),
- UsernameValue(user, true) | rpl::map([=](TextWithEntities u) {
- return u.text.isEmpty()
- ? TextWithEntities()
- : Ui::Text::Link(u, UsernameUrl(user, u.text.mid(1)));
- }),
- QString(),
- st::infoProfileLabeledUsernamePadding);
- const auto callback = UsernamesLinkCallback(
- _peer,
- controller,
- QString());
- usernameLine.text->overrideLinkClickHandler(callback);
- usernameLine.subtext->overrideLinkClickHandler(callback);
- usernameLine.text->setContextMenuHook(lnkHook);
- usernameLine.subtext->setContextMenuHook(lnkHook);
- const auto qrButton = Ui::CreateChild<Ui::IconButton>(
- usernameLine.text->parentWidget(),
- st::infoProfileLabeledButtonQr);
- const auto rightSkip = 0;// st::infoProfileLabeledButtonQrRightSkip;
- fitLabelToButton(qrButton, usernameLine.text, rightSkip);
- fitLabelToButton(qrButton, usernameLine.subtext, rightSkip);
- qrButton->setClickedCallback([=] {
- controller->show(
- Box(Ui::FillPeerQrBox, user, std::nullopt, nullptr));
- return false;
- });
- if (!user->isBot()) {
- tracker.track(result->add(
- CreateBirthday(result, controller, user)));
- tracker.track(result->add(CreateWorkingHours(result, user)));
- auto locationText = user->session().changes().peerFlagsValue(
- user,
- Data::PeerUpdate::Flag::BusinessDetails
- ) | rpl::map([=] {
- const auto &details = user->businessDetails();
- if (!details.location) {
- return TextWithEntities();
- } else if (!details.location.point) {
- return TextWithEntities{ details.location.address };
- }
- return Ui::Text::Link(
- TextUtilities::SingleLine(details.location.address),
- LocationClickHandler::Url(*details.location.point));
- });
- addInfoOneLine(
- tr::lng_info_location_label(),
- std::move(locationText),
- QString()
- ).text->setLinksTrusted();
- }
- AddMainButton(
- result,
- tr::lng_info_add_as_contact(),
- CanAddContactValue(user),
- [=] { controller->window().show(Box(EditContactBox, controller, user)); },
- tracker);
- } else {
- const auto topicRootId = _topic ? _topic->rootId() : 0;
- const auto addToLink = topicRootId
- ? ('/' + QString::number(topicRootId.bare))
- : QString();
- auto linkText = LinkValue(
- _peer,
- true
- ) | rpl::map([=](const LinkWithUrl &link) {
- const auto text = link.text;
- return text.isEmpty()
- ? TextWithEntities()
- : Ui::Text::Link(
- (text.startsWith(u"https://"_q)
- ? text.mid(u"https://"_q.size())
- : text) + addToLink,
- (addToLink.isEmpty() ? link.url : (text + addToLink)));
- });
- const auto linkLine = addInfoOneLine(
- (topicRootId
- ? tr::lng_info_link_label(Ui::Text::WithEntities)
- : UsernamesSubtext(_peer, tr::lng_info_link_label())),
- std::move(linkText),
- QString());
- const auto controller = _controller->parentController();
- const auto linkCallback = UsernamesLinkCallback(
- _peer,
- controller,
- addToLink);
- linkLine.text->overrideLinkClickHandler(linkCallback);
- linkLine.subtext->overrideLinkClickHandler(linkCallback);
- linkLine.text->setContextMenuHook(lnkHook);
- linkLine.subtext->setContextMenuHook(lnkHook);
- {
- const auto qr = Ui::CreateChild<Ui::IconButton>(
- linkLine.text->parentWidget(),
- st::infoProfileLabeledButtonQr);
- const auto rightSkip = 0;// st::infoProfileLabeledButtonQrRightSkip;
- fitLabelToButton(qr, linkLine.text, rightSkip);
- fitLabelToButton(qr, linkLine.subtext, rightSkip);
- qr->setClickedCallback([=, peer = _peer] {
- controller->show(
- Box(Ui::FillPeerQrBox, peer, std::nullopt, nullptr));
- return false;
- });
- }
- if (const auto channel = _topic ? nullptr : _peer->asChannel()) {
- auto locationText = LocationValue(
- channel
- ) | rpl::map([](const ChannelLocation *location) {
- return location
- ? Ui::Text::Link(
- TextUtilities::SingleLine(location->address),
- LocationClickHandler::Url(location->point))
- : TextWithEntities();
- });
- addInfoOneLine(
- tr::lng_info_location_label(),
- std::move(locationText),
- QString()
- ).text->setLinksTrusted();
- }
- const auto about = addInfoLine(tr::lng_info_about_label(), _topic
- ? rpl::single(TextWithEntities())
- : AboutWithIdValue(_peer));
- if (!_topic) {
- addTranslateToMenu(about.text, AboutWithIdValue(_peer));
- }
- }
- if (!_peer->isSelf()) {
- // No notifications toggle for Self => no separator.
- const auto user = _peer->asUser();
- const auto app = user && user->botInfo && user->botInfo->hasMainApp;
- const auto padding = app
- ? QMargins(
- st::infoOpenAppMargin.left(),
- st::infoProfileSeparatorPadding.top(),
- st::infoOpenAppMargin.right(),
- 0)
- : st::infoProfileSeparatorPadding;
- result->add(object_ptr<Ui::SlideWrap<>>(
- result,
- object_ptr<Ui::PlainShadow>(result),
- padding)
- )->setDuration(
- st::infoSlideDuration
- )->toggleOn(
- std::move(tracker).atLeastOneShownValue()
- );
- }
- object_ptr<FloatingIcon>(
- result,
- st::infoIconInformation,
- st::infoInformationIconPosition);
- return result;
- }
- object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel(
- not_null<UserData*> user) {
- auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _wrap,
- object_ptr<Ui::VerticalLayout>(_wrap));
- const auto container = result->entity();
- const auto window = _controller->parentController();
- result->toggleOn(PersonalChannelValue(
- user
- ) | rpl::map(rpl::mappers::_1 != nullptr));
- result->finishAnimating();
- auto channel = PersonalChannelValue(
- user
- ) | rpl::start_spawning(result->lifetime());
- const auto channelLabelFactory = [=](rpl::producer<ChannelData*> c) {
- return rpl::combine(
- tr::lng_info_personal_channel_label(Ui::Text::WithEntities),
- std::move(c)
- ) | rpl::map([](TextWithEntities &&text, ChannelData *channel) {
- const auto count = channel ? channel->membersCount() : 0;
- if (count > 1) {
- text.append(
- QString::fromUtf8(" \xE2\x80\xA2 ")
- ).append(tr::lng_chat_status_subscribers(
- tr::now,
- lt_count_decimal,
- count));
- }
- return text;
- });
- };
- {
- const auto onlyChannelWrap = container->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- container,
- object_ptr<Ui::VerticalLayout>(container)));
- onlyChannelWrap->toggleOn(PersonalChannelValue(user) | rpl::map([=] {
- return user->personalChannelId()
- && !user->personalChannelMessageId();
- }));
- onlyChannelWrap->finishAnimating();
- auto text = rpl::duplicate(
- channel
- ) | rpl::map([=](ChannelData *channel) {
- return channel ? NameValue(channel) : rpl::single(QString());
- }) | rpl::flatten_latest() | rpl::map([](const QString &name) {
- return name.isEmpty() ? TextWithEntities() : Ui::Text::Link(name);
- });
- auto line = CreateTextWithLabel(
- result,
- channelLabelFactory(rpl::duplicate(channel)),
- std::move(text),
- st::infoLabel,
- st::infoLabeled,
- st::infoProfileLabeledPadding);
- onlyChannelWrap->entity()->add(std::move(line.wrap));
- line.text->setClickHandlerFilter([=](
- const ClickHandlerPtr &handler,
- Qt::MouseButton button) {
- if (const auto channelId = user->personalChannelId()) {
- window->showPeerInfo(peerFromChannel(channelId));
- }
- return false;
- });
- object_ptr<FloatingIcon>(
- onlyChannelWrap,
- st::infoIconMediaChannel,
- st::infoPersonalChannelIconPosition);
- Ui::AddDivider(onlyChannelWrap->entity());
- }
- {
- const auto messageChannelWrap = container->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- container,
- object_ptr<Ui::VerticalLayout>(container)));
- messageChannelWrap->toggleOn(PersonalChannelValue(
- user
- ) | rpl::map([=] {
- return user->personalChannelId()
- && user->personalChannelMessageId();
- }));
- messageChannelWrap->finishAnimating();
- messageChannelWrap->toggledValue(
- ) | rpl::filter(rpl::mappers::_1) | rpl::start_with_next([=] {
- messageChannelWrap->resizeToWidth(messageChannelWrap->width());
- }, messageChannelWrap->lifetime());
- const auto clear = [=] {
- while (messageChannelWrap->entity()->count()) {
- delete messageChannelWrap->entity()->widgetAt(0);
- }
- };
- const auto rebuild = [=](
- not_null<HistoryItem*> item,
- anim::type animated) {
- const auto &stUserpic = st::infoPersonalChannelUserpic;
- const auto &stLabeled = st::infoProfileLabeledPadding;
- messageChannelWrap->toggle(false, anim::type::instant);
- clear();
- Ui::AddSkip(messageChannelWrap->entity());
- const auto inner = messageChannelWrap->entity()->add(
- object_ptr<Ui::VerticalLayout>(messageChannelWrap->entity()));
- const auto line = inner->add(
- object_ptr<Ui::FixedHeightWidget>(
- inner,
- stUserpic.photoSize + rect::m::sum::v(stLabeled)));
- const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
- line,
- item->history()->peer,
- st::infoPersonalChannelUserpic);
- userpic->moveToLeft(
- -st::infoPersonalChannelUserpicSkip
- + (stLabeled.left() - stUserpic.photoSize) / 2,
- stLabeled.top());
- userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
- const auto date = Ui::CreateChild<Ui::FlatLabel>(
- line,
- Ui::FormatDialogsDate(ItemDateTime(item)),
- st::infoPersonalChannelDateLabel);
- const auto name = Ui::CreateChild<Ui::FlatLabel>(
- line,
- NameValue(item->history()->peer),
- st::infoPersonalChannelNameLabel);
- const auto preview = Ui::CreateChild<Ui::RpWidget>(line);
- auto &lifetime = preview->lifetime();
- using namespace Dialogs::Ui;
- struct State {
- MessageView view;
- HistoryItem *item = nullptr;
- rpl::lifetime lifetime;
- };
- const auto state = lifetime.make_state<State>();
- state->item = item;
- item->history()->session().changes().realtimeMessageUpdates(
- Data::MessageUpdate::Flag::Destroyed
- ) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
- if (update.item == state->item) {
- state->lifetime.destroy();
- state->item = nullptr;
- preview->update();
- }
- }, state->lifetime);
- preview->resize(0, st::infoLabeled.style.font->height);
- preview->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = Painter(preview);
- const auto item = state->item;
- if (!item) {
- p.setPen(st::infoPersonalChannelDateLabel.textFg);
- p.setBrush(Qt::NoBrush);
- p.setFont(st::infoPersonalChannelDateLabel.style.font);
- p.drawText(
- preview->rect(),
- tr::lng_deleted_message(tr::now),
- style::al_left);
- return;
- }
- if (!state->view.prepared(item, nullptr)) {
- const auto repaint = [=] { preview->update(); };
- state->view.prepare(item, nullptr, repaint, {});
- }
- state->view.paint(p, preview->rect(), {
- .st = &st::defaultDialogRow,
- .currentBg = st::boxBg->b,
- });
- }, preview->lifetime());
- line->sizeValue() | rpl::filter_size(
- ) | rpl::start_with_next([=](const QSize &size) {
- const auto left = stLabeled.left();
- const auto right = st::infoPersonalChannelDateSkip;
- const auto top = stLabeled.top();
- date->moveToRight(right, top, size.width());
- name->resizeToWidth(size.width()
- - left
- - date->width()
- - st::defaultVerticalListSkip
- - right);
- name->moveToLeft(left, top);
- preview->resize(
- size.width() - left - right,
- st::infoLabeled.style.font->height);
- preview->moveToLeft(
- left,
- size.height() - stLabeled.bottom() - preview->height());
- }, preview->lifetime());
- {
- inner->add(
- object_ptr<Ui::FlatLabel>(
- inner,
- channelLabelFactory(
- rpl::single(item->history()->peer->asChannel())),
- st::infoLabel),
- QMargins(
- st::infoProfileLabeledPadding.left(),
- 0,
- st::infoProfileLabeledPadding.right(),
- st::infoProfileLabeledPadding.bottom()));
- }
- {
- const auto button = Ui::CreateSimpleRectButton(
- messageChannelWrap->entity(),
- st::defaultRippleAnimation);
- inner->geometryValue(
- ) | rpl::start_with_next([=](const QRect &rect) {
- button->setGeometry(rect);
- }, button->lifetime());
- button->setClickedCallback([=, msg = item->fullId().msg] {
- window->showPeerHistory(
- item->history()->peer,
- Window::SectionShow::Way::Forward,
- msg);
- });
- button->lower();
- inner->lifetime().make_state<base::unique_qptr<Ui::RpWidget>>(
- button);
- }
- inner->setAttribute(Qt::WA_TransparentForMouseEvents);
- Ui::AddSkip(messageChannelWrap->entity());
- Ui::AddDivider(messageChannelWrap->entity());
- Ui::ToggleChildrenVisibility(messageChannelWrap->entity(), true);
- Ui::ToggleChildrenVisibility(line, true);
- messageChannelWrap->toggle(true, animated);
- };
- rpl::duplicate(
- channel
- ) | rpl::start_with_next([=](ChannelData *channel) {
- clear();
- if (!channel) {
- return;
- }
- const auto id = FullMsgId(
- channel->id,
- user->personalChannelMessageId());
- if (const auto item = user->session().data().message(id)) {
- return rebuild(item, anim::type::instant);
- }
- user->session().api().requestMessageData(
- channel,
- user->personalChannelMessageId(),
- crl::guard(container, [=] {
- if (const auto i = user->session().data().message(id)) {
- rebuild(i, anim::type::normal);
- }
- }));
- }, messageChannelWrap->lifetime());
- }
- return result;
- }
- object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
- const auto peer = _peer;
- const auto topicRootId = _topic ? _topic->rootId() : MsgId();
- const auto makeThread = [=] {
- return topicRootId
- ? static_cast<Data::Thread*>(peer->forumTopicFor(topicRootId))
- : peer->owner().history(peer).get();
- };
- auto result = object_ptr<Ui::SettingsButton>(
- _wrap,
- tr::lng_profile_enable_notifications(),
- st::infoNotificationsButton);
- result->toggleOn(_topic
- ? NotificationsEnabledValue(_topic)
- : NotificationsEnabledValue(peer), true);
- result->setAcceptBoth();
- const auto notifySettings = &peer->owner().notifySettings();
- MuteMenu::SetupMuteMenu(
- result.data(),
- result->clicks(
- ) | rpl::filter([=](Qt::MouseButton button) {
- if (button == Qt::RightButton) {
- return true;
- }
- const auto topic = topicRootId
- ? peer->forumTopicFor(topicRootId)
- : nullptr;
- Assert(!topicRootId || topic != nullptr);
- const auto is = topic
- ? notifySettings->isMuted(topic)
- : notifySettings->isMuted(peer);
- if (is) {
- if (topic) {
- notifySettings->update(topic, { .unmute = true });
- } else {
- notifySettings->update(peer, { .unmute = true });
- }
- return false;
- } else {
- return true;
- }
- }) | rpl::to_empty,
- makeThread,
- _controller->uiShow());
- object_ptr<FloatingIcon>(
- result,
- st::infoIconNotifications,
- st::infoNotificationsIconPosition);
- return result;
- }
- void DetailsFiller::setupAboutVerification() {
- const auto peer = _peer;
- const auto inner = _wrap->add(object_ptr<Ui::VerticalLayout>(_wrap));
- peer->session().changes().peerFlagsValue(
- peer,
- Data::PeerUpdate::Flag::VerifyInfo
- ) | rpl::start_with_next([=] {
- const auto info = peer->botVerifyDetails();
- while (inner->count()) {
- delete inner->widgetAt(0);
- }
- if (!info) {
- Ui::AddDivider(inner);
- } else if (!info->description.empty()) {
- Ui::AddDividerText(inner, rpl::single(info->description));
- }
- inner->resizeToWidth(inner->width());
- }, inner->lifetime());
- }
- void DetailsFiller::setupMainApp() {
- const auto button = _wrap->add(
- object_ptr<Ui::RoundButton>(
- _wrap,
- tr::lng_profile_open_app(),
- st::infoOpenApp),
- st::infoOpenAppMargin);
- button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
- const auto user = _peer->asUser();
- const auto controller = _controller->parentController();
- button->setClickedCallback([=] {
- user->session().attachWebView().open({
- .bot = user,
- .context = {
- .controller = controller,
- .maySkipConfirmation = true,
- },
- .source = InlineBots::WebViewSourceBotProfile(),
- });
- });
- const auto url = tr::lng_mini_apps_tos_url(tr::now);
- Ui::AddDividerText(
- _wrap,
- tr::lng_profile_open_app_about(
- lt_terms,
- tr::lng_profile_open_app_terms() | Ui::Text::ToLink(url),
- Ui::Text::WithEntities)
- )->setClickHandlerFilter([=](const auto &...) {
- UrlClickHandler::Open(url);
- return false;
- });
- Ui::AddSkip(_wrap);
- }
- void DetailsFiller::setupBotPermissions() {
- AddSkip(_wrap);
- AddSubsectionTitle(_wrap, tr::lng_profile_bot_permissions_title());
- const auto emoji = _wrap->add(
- object_ptr<Ui::SettingsButton>(
- _wrap,
- tr::lng_profile_bot_emoji_status_access(),
- st::infoSharedMediaButton));
- object_ptr<Profile::FloatingIcon>(
- emoji,
- st::infoIconEmojiStatusAccess,
- st::infoSharedMediaButtonIconPosition);
- const auto user = _peer->asUser();
- emoji->toggleOn(
- rpl::single(bool(user->botInfo->canManageEmojiStatus))
- )->toggledValue() | rpl::filter([=](bool allowed) {
- return allowed != user->botInfo->canManageEmojiStatus;
- }) | rpl::start_with_next([=](bool allowed) {
- user->botInfo->canManageEmojiStatus = allowed;
- const auto session = &user->session();
- session->api().request(MTPbots_ToggleUserEmojiStatusPermission(
- user->inputUser,
- MTP_bool(allowed)
- )).send();
- }, emoji->lifetime());
- AddSkip(_wrap);
- AddDivider(_wrap);
- AddSkip(_wrap);
- }
- void DetailsFiller::setupMainButtons() {
- auto wrapButtons = [=](auto &&callback) {
- auto topSkip = _wrap->add(CreateSlideSkipWidget(_wrap));
- auto tracker = callback();
- topSkip->toggleOn(std::move(tracker).atLeastOneShownValue());
- };
- if (_topic) {
- wrapButtons([=] {
- return fillTopicButtons();
- });
- } else if (const auto user = _peer->asUser()) {
- wrapButtons([=] {
- return fillUserButtons(user);
- });
- } else if (const auto channel = _peer->asChannel()) {
- if (channel->isMegagroup()) {
- wrapButtons([=] {
- return fillDiscussionButtons(channel);
- });
- } else {
- wrapButtons([=] {
- return fillChannelButtons(channel);
- });
- }
- }
- }
- void DetailsFiller::addReportReaction(Ui::MultiSlideTracker &tracker) {
- v::match(_origin.data, [&](GroupReactionOrigin data) {
- const auto user = _peer->asUser();
- if (_peer->isSelf()) {
- return;
- #if 0 // Only public groups allow reaction reports for now.
- } else if (const auto chat = data.group->asChat()) {
- const auto ban = chat->canBanMembers()
- && (!user || !chat->admins.contains(_peer))
- && (!user || chat->creator != user->id);
- addReportReaction(data, ban, tracker);
- #endif
- } else if (const auto channel = data.group->asMegagroup()) {
- if (channel->isPublic()) {
- const auto ban = channel->canBanMembers()
- && (!user || !channel->mgInfo->admins.contains(user->id))
- && (!user || channel->mgInfo->creator != user);
- addReportReaction(data, ban, tracker);
- }
- }
- }, [](const auto &) {});
- }
- void DetailsFiller::addReportReaction(
- GroupReactionOrigin data,
- bool ban,
- Ui::MultiSlideTracker &tracker) {
- const auto peer = _peer;
- if (!peer) {
- return;
- }
- const auto controller = _controller->parentController();
- const auto forceHidden = std::make_shared<rpl::variable<bool>>(false);
- const auto user = peer->asUser();
- auto shown = user
- ? rpl::combine(
- Info::Profile::IsContactValue(user),
- forceHidden->value(),
- !rpl::mappers::_1 && !rpl::mappers::_2
- ) | rpl::type_erased()
- : (forceHidden->value() | rpl::map(!rpl::mappers::_1));
- const auto sent = [=] {
- *forceHidden = true;
- };
- AddMainButton(
- _wrap,
- (ban
- ? tr::lng_report_and_ban()
- : tr::lng_report_reaction()),
- std::move(shown),
- [=] { controller->show(
- Box(ReportReactionBox, controller, peer, data, ban, sent)); },
- tracker,
- st::infoMainButtonAttention);
- }
- Ui::MultiSlideTracker DetailsFiller::fillTopicButtons() {
- using namespace rpl::mappers;
- Ui::MultiSlideTracker tracker;
- const auto window = _controller->parentController();
- const auto forum = _topic->forum();
- auto showTopicsVisible = rpl::combine(
- window->adaptive().oneColumnValue(),
- window->shownForum().value(),
- _1 || (_2 != forum));
- AddMainButton(
- _wrap,
- tr::lng_forum_show_topics_list(),
- std::move(showTopicsVisible),
- [=] { window->showForum(forum); },
- tracker);
- return tracker;
- }
- Ui::MultiSlideTracker DetailsFiller::fillUserButtons(
- not_null<UserData*> user) {
- using namespace rpl::mappers;
- Ui::MultiSlideTracker tracker;
- if (user->isSelf()) {
- return tracker;
- }
- auto window = _controller->parentController();
- auto addSendMessageButton = [&] {
- auto activePeerValue = window->activeChatValue(
- ) | rpl::map([](Dialogs::Key key) {
- return key.peer();
- });
- auto sendMessageVisible = rpl::combine(
- _controller->wrapValue(),
- std::move(activePeerValue),
- (_1 != Wrap::Side) || (_2 != user));
- auto sendMessage = [window, user] {
- window->showPeerHistory(
- user,
- Window::SectionShow::Way::Forward);
- };
- AddMainButton(
- _wrap,
- tr::lng_profile_send_message(),
- std::move(sendMessageVisible),
- std::move(sendMessage),
- tracker);
- };
- addSendMessageButton();
- addReportReaction(tracker);
- return tracker;
- }
- Ui::MultiSlideTracker DetailsFiller::fillChannelButtons(
- not_null<ChannelData*> channel) {
- using namespace rpl::mappers;
- Ui::MultiSlideTracker tracker;
- auto window = _controller->parentController();
- auto activePeerValue = window->activeChatValue(
- ) | rpl::map([](Dialogs::Key key) {
- return key.peer();
- });
- auto viewChannelVisible = rpl::combine(
- _controller->wrapValue(),
- std::move(activePeerValue),
- (_1 != Wrap::Side) || (_2 != channel));
- auto viewChannel = [=] {
- window->showPeerHistory(
- channel,
- Window::SectionShow::Way::Forward);
- };
- AddMainButton(
- _wrap,
- tr::lng_profile_view_channel(),
- std::move(viewChannelVisible),
- std::move(viewChannel),
- tracker);
- return tracker;
- }
- Ui::MultiSlideTracker DetailsFiller::fillDiscussionButtons(
- not_null<ChannelData*> channel) {
- using namespace rpl::mappers;
- Ui::MultiSlideTracker tracker;
- auto window = _controller->parentController();
- auto viewDiscussionVisible = window->dialogsEntryStateValue(
- ) | rpl::map([=](const Dialogs::EntryState &state) {
- const auto history = state.key.history();
- return (state.section == Dialogs::EntryState::Section::Replies)
- && history
- && (history->peer == channel);
- });
- auto viewDiscussion = [=] {
- window->showPeerHistory(
- channel,
- Window::SectionShow::Way::Forward);
- };
- AddMainButton(
- _wrap,
- tr::lng_profile_view_discussion(),
- std::move(viewDiscussionVisible),
- std::move(viewDiscussion),
- tracker);
- return tracker;
- }
- object_ptr<Ui::RpWidget> DetailsFiller::fill() {
- Expects(!_topic || !_topic->creating());
- if (!_topic) {
- setupAboutVerification();
- } else {
- add(object_ptr<Ui::BoxContentDivider>(_wrap));
- }
- if (const auto user = _peer->asUser()) {
- add(setupPersonalChannel(user));
- }
- add(CreateSkipWidget(_wrap));
- add(setupInfo());
- if (const auto user = _peer->asUser()) {
- if (const auto info = user->botInfo.get()) {
- if (info->hasMainApp) {
- setupMainApp();
- }
- if (info->canManageEmojiStatus) {
- setupBotPermissions();
- }
- }
- }
- if (!_peer->isSelf()) {
- add(setupMuteToggle());
- }
- setupMainButtons();
- add(CreateSkipWidget(_wrap));
- return std::move(_wrap);
- }
- ActionsFiller::ActionsFiller(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer)
- : _controller(controller)
- , _parent(parent)
- , _peer(peer) {
- }
- void ActionsFiller::addAffiliateProgram(not_null<UserData*> user) {
- if (!user->isBot()) {
- return;
- }
- const auto wrap = _wrap->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _wrap.data(),
- object_ptr<Ui::VerticalLayout>(_wrap.data())));
- const auto inner = wrap->entity();
- auto program = user->session().changes().peerFlagsValue(
- user,
- Data::PeerUpdate::Flag::StarRefProgram
- ) | rpl::map([=] {
- return user->botInfo->starRefProgram;
- }) | rpl::start_spawning(inner->lifetime());
- auto commission = rpl::duplicate(
- program
- ) | rpl::filter([=](StarRefProgram program) {
- return program.commission > 0;
- }) | rpl::map([=](StarRefProgram program) {
- return Info::BotStarRef::FormatCommission(program.commission);
- });
- const auto show = _controller->uiShow();
- struct StarRefRecipients {
- std::vector<not_null<PeerData*>> list;
- bool requested = false;
- Fn<void()> open;
- };
- const auto recipients = std::make_shared<StarRefRecipients>();
- recipients->open = [=] {
- if (!recipients->list.empty()) {
- const auto program = user->botInfo->starRefProgram;
- show->show(Info::BotStarRef::JoinStarRefBox(
- { user, { program } },
- user->session().user(),
- recipients->list));
- } else if (!recipients->requested) {
- recipients->requested = true;
- const auto done = [=](std::vector<not_null<PeerData*>> list) {
- recipients->list = std::move(list);
- recipients->open();
- };
- Info::BotStarRef::ResolveRecipients(&user->session(), done);
- }
- };
- inner->add(EditPeerInfoBox::CreateButton(
- inner,
- tr::lng_manage_peer_bot_star_ref(),
- rpl::duplicate(commission),
- recipients->open,
- st::infoSharedMediaCountButton,
- { .icon = &st::menuIconSharing, .newBadge = true }));
- Ui::AddSkip(inner);
- Ui::AddDividerText(
- inner,
- tr::lng_manage_peer_bot_star_ref_about(
- lt_bot,
- rpl::single(TextWithEntities{ user->name() }),
- lt_amount,
- rpl::duplicate(commission) | Ui::Text::ToWithEntities(),
- Ui::Text::RichLangValue));
- Ui::AddSkip(inner);
- wrap->toggleOn(std::move(
- program
- ) | rpl::map([](StarRefProgram program) {
- return program.commission > 0;
- }));
- wrap->finishAnimating();
- }
- void ActionsFiller::addBalanceActions(not_null<UserData*> user) {
- const auto wrap = _wrap->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _wrap.data(),
- object_ptr<Ui::VerticalLayout>(_wrap.data())));
- const auto inner = wrap->entity();
- Ui::AddSubsectionTitle(inner, tr::lng_manage_peer_bot_balance());
- auto currencyBalance = AddCurrencyAction(user, inner, _controller);
- auto creditsBalance = AddCreditsAction(user, inner, _controller);
- Ui::AddSkip(inner);
- Ui::AddDivider(inner);
- Ui::AddSkip(inner);
- wrap->toggleOn(
- rpl::combine(
- std::move(currencyBalance),
- std::move(creditsBalance)
- ) | rpl::map((rpl::mappers::_1 > 0)
- || (rpl::mappers::_2 > StarsAmount(0))));
- }
- void ActionsFiller::addInviteToGroupAction(not_null<UserData*> user) {
- const auto notEmpty = [](const QString &value) {
- return !value.isEmpty();
- };
- const auto controller = _controller->parentController();
- AddActionButton(
- _wrap,
- InviteToChatButton(user) | rpl::filter(notEmpty),
- InviteToChatButton(user) | rpl::map(notEmpty),
- [=] { AddBotToGroupBoxController::Start(controller, user); },
- &st::infoIconAddMember);
- const auto about = _wrap->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _wrap.data(),
- object_ptr<Ui::VerticalLayout>(_wrap.data())));
- about->toggleOn(InviteToChatAbout(user) | rpl::map(notEmpty));
- Ui::AddSkip(about->entity());
- Ui::AddDividerText(
- about->entity(),
- InviteToChatAbout(user) | rpl::filter(notEmpty));
- Ui::AddSkip(about->entity());
- about->finishAnimating();
- }
- void ActionsFiller::addShareContactAction(not_null<UserData*> user) {
- const auto controller = _controller->parentController();
- AddActionButton(
- _wrap,
- tr::lng_info_share_contact(),
- CanShareContactValue(user),
- [=] { Window::PeerMenuShareContactBox(controller, user); },
- &st::infoIconShare);
- }
- void ActionsFiller::addEditContactAction(not_null<UserData*> user) {
- const auto controller = _controller->parentController();
- AddActionButton(
- _wrap,
- tr::lng_info_edit_contact(),
- IsContactValue(user),
- [=] { controller->window().show(Box(EditContactBox, controller, user)); },
- &st::infoIconEdit);
- }
- void ActionsFiller::addDeleteContactAction(not_null<UserData*> user) {
- const auto controller = _controller->parentController();
- AddActionButton(
- _wrap,
- tr::lng_info_delete_contact(),
- IsContactValue(user),
- [=] { Window::PeerMenuDeleteContact(controller, user); },
- &st::infoIconDelete);
- }
- void ActionsFiller::addFastButtonsMode(not_null<UserData*> user) {
- Expects(user->isBot());
- const auto bots = &user->session().fastButtonsBots();
- const auto button = _wrap->add(object_ptr<Ui::SettingsButton>(
- _wrap,
- rpl::single(u"Fast buttons mode"_q),
- st::infoSharedMediaButton));
- object_ptr<Info::Profile::FloatingIcon>(
- button,
- st::infoIconMediaBot,
- st::infoSharedMediaButtonIconPosition);
- AddSkip(_wrap);
- AddDivider(_wrap);
- AddSkip(_wrap);
- button->toggleOn(bots->enabledValue(user));
- button->toggledValue(
- ) | rpl::filter([=](bool value) {
- return value != bots->enabled(user);
- }) | rpl::start_with_next([=](bool value) {
- bots->setEnabled(user, value);
- }, button->lifetime());
- }
- void ActionsFiller::addBotCommandActions(not_null<UserData*> user) {
- if (FastButtonsMode()) {
- addFastButtonsMode(user);
- }
- const auto window = _controller->parentController();
- const auto findBotCommand = [user](const QString &command) {
- if (!user->isBot()) {
- return QString();
- }
- for (const auto &data : user->botInfo->commands) {
- const auto isSame = !data.command.compare(
- command,
- Qt::CaseInsensitive);
- if (isSame) {
- return data.command;
- }
- }
- return QString();
- };
- const auto hasBotCommandValue = [=](const QString &command) {
- return user->session().changes().peerFlagsValue(
- user,
- Data::PeerUpdate::Flag::BotCommands
- ) | rpl::map([=] {
- return !findBotCommand(command).isEmpty();
- });
- };
- const auto makeOtherContext = [=] {
- return QVariant::fromValue(ClickHandlerContext{
- .sessionWindow = base::make_weak(window),
- .peer = user,
- });
- };
- const auto sendBotCommand = [=](const QString &command) {
- const auto original = findBotCommand(command);
- if (original.isEmpty()) {
- return false;
- }
- BotCommandClickHandler('/' + original).onClick(ClickContext{
- Qt::LeftButton,
- makeOtherContext()
- });
- return true;
- };
- const auto addBotCommand = [=](
- rpl::producer<QString> text,
- const QString &command,
- const style::icon *icon = nullptr) {
- AddActionButton(
- _wrap,
- std::move(text),
- hasBotCommandValue(command),
- [=] { sendBotCommand(command); },
- icon);
- };
- addBotCommand(
- tr::lng_profile_bot_help(),
- u"help"_q,
- &st::infoIconInformation);
- addBotCommand(tr::lng_profile_bot_settings(), u"settings"_q);
- //addBotCommand(tr::lng_profile_bot_privacy(), u"privacy"_q);
- const auto openUrl = [=](const QString &url) {
- Core::App().iv().openWithIvPreferred(
- &user->session(),
- url,
- makeOtherContext());
- };
- const auto openPrivacyPolicy = [=] {
- if (const auto info = user->botInfo.get()) {
- if (!info->privacyPolicyUrl.isEmpty()) {
- openUrl(info->privacyPolicyUrl);
- return;
- }
- }
- if (!sendBotCommand(u"privacy"_q)) {
- openUrl(tr::lng_profile_bot_privacy_url(tr::now));
- }
- };
- AddActionButton(
- _wrap,
- tr::lng_profile_bot_privacy(),
- rpl::single(true),
- openPrivacyPolicy,
- nullptr);
- }
- void ActionsFiller::addReportAction() {
- const auto peer = _peer;
- const auto controller = _controller->parentController();
- const auto report = [=] {
- ShowReportMessageBox(controller->uiShow(), peer, {}, {});
- };
- AddActionButton(
- _wrap,
- tr::lng_profile_report(),
- rpl::single(true),
- report,
- &st::infoIconReport,
- st::infoBlockButton);
- }
- void ActionsFiller::addBlockAction(not_null<UserData*> user) {
- const auto controller = _controller->parentController();
- const auto window = &controller->window();
- auto text = user->session().changes().peerFlagsValue(
- user,
- Data::PeerUpdate::Flag::IsBlocked
- ) | rpl::map([=] {
- switch (user->blockStatus()) {
- case UserData::BlockStatus::Blocked:
- return ((user->isBot() && !user->isSupport())
- ? tr::lng_profile_restart_bot
- : tr::lng_profile_unblock_user)();
- case UserData::BlockStatus::NotBlocked:
- default:
- return ((user->isBot() && !user->isSupport())
- ? tr::lng_profile_block_bot
- : tr::lng_profile_block_user)();
- }
- }) | rpl::flatten_latest(
- ) | rpl::start_spawning(_wrap->lifetime());
- auto toggleOn = rpl::duplicate(
- text
- ) | rpl::map([](const QString &text) {
- return !text.isEmpty();
- });
- auto callback = [=] {
- if (user->isBlocked()) {
- const auto show = controller->uiShow();
- Window::PeerMenuUnblockUserWithBotRestart(show, user);
- if (user->isBot()) {
- controller->showPeerHistory(user);
- }
- } else if (user->isBot()) {
- user->session().api().blockedPeers().block(user);
- } else {
- window->show(Box(
- Window::PeerMenuBlockUserBox,
- window,
- user,
- v::null,
- v::null));
- }
- };
- AddActionButton(
- _wrap,
- rpl::duplicate(text),
- std::move(toggleOn),
- std::move(callback),
- &st::infoIconBlock,
- st::infoBlockButton);
- }
- void ActionsFiller::addLeaveChannelAction(not_null<ChannelData*> channel) {
- Expects(_controller->parentController());
- AddActionButton(
- _wrap,
- tr::lng_profile_leave_channel(),
- AmInChannelValue(channel),
- Window::DeleteAndLeaveHandler(
- _controller->parentController(),
- channel),
- &st::infoIconLeave);
- }
- void ActionsFiller::addJoinChannelAction(
- not_null<ChannelData*> channel) {
- using namespace rpl::mappers;
- auto joinVisible = AmInChannelValue(channel)
- | rpl::map(!_1)
- | rpl::start_spawning(_wrap->lifetime());
- AddActionButton(
- _wrap,
- tr::lng_profile_join_channel(),
- rpl::duplicate(joinVisible),
- [=] { channel->session().api().joinChannel(channel); },
- &st::infoIconAddMember);
- _wrap->add(object_ptr<Ui::SlideWrap<Ui::FixedHeightWidget>>(
- _wrap,
- CreateSkipWidget(
- _wrap,
- st::infoBlockButtonSkip))
- )->setDuration(
- st::infoSlideDuration
- )->toggleOn(
- rpl::duplicate(joinVisible)
- );
- }
- void ActionsFiller::fillUserActions(not_null<UserData*> user) {
- if (user->isBot()) {
- addAffiliateProgram(user);
- addBalanceActions(user);
- addInviteToGroupAction(user);
- }
- addShareContactAction(user);
- if (!user->isSelf()) {
- addEditContactAction(user);
- addDeleteContactAction(user);
- }
- if (!user->isSelf() && !user->isSupport() && !user->isVerifyCodes()) {
- if (user->isBot()) {
- addBotCommandActions(user);
- }
- _wrap->add(CreateSkipWidget(
- _wrap,
- st::infoBlockButtonSkip));
- if (user->isBot()) {
- addReportAction();
- }
- addBlockAction(user);
- }
- }
- void ActionsFiller::fillChannelActions(
- not_null<ChannelData*> channel) {
- using namespace rpl::mappers;
- addJoinChannelAction(channel);
- addLeaveChannelAction(channel);
- if (!channel->amCreator()) {
- addReportAction();
- }
- }
- object_ptr<Ui::RpWidget> ActionsFiller::fill() {
- auto wrapResult = [=](auto &&callback) {
- _wrap = object_ptr<Ui::VerticalLayout>(_parent);
- _wrap->add(CreateSkipWidget(_wrap));
- callback();
- _wrap->add(CreateSkipWidget(_wrap));
- return std::move(_wrap);
- };
- if (auto user = _peer->asUser()) {
- return wrapResult([=] {
- fillUserActions(user);
- });
- } else if (auto channel = _peer->asChannel()) {
- if (channel->isMegagroup()) {
- return { nullptr };
- }
- return wrapResult([=] {
- fillChannelActions(channel);
- });
- }
- return { nullptr };
- }
- } // namespace
- const char kOptionShowPeerIdBelowAbout[] = "show-peer-id-below-about";
- object_ptr<Ui::RpWidget> SetupDetails(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer,
- Origin origin) {
- DetailsFiller filler(controller, parent, peer, origin);
- return filler.fill();
- }
- object_ptr<Ui::RpWidget> SetupDetails(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<Data::ForumTopic*> topic) {
- DetailsFiller filler(controller, parent, topic);
- return filler.fill();
- }
- object_ptr<Ui::RpWidget> SetupActions(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer) {
- ActionsFiller filler(controller, parent, peer);
- return filler.fill();
- }
- void SetupAddChannelMember(
- not_null<Window::SessionNavigation*> navigation,
- not_null<Ui::RpWidget*> parent,
- not_null<ChannelData*> channel) {
- auto add = Ui::CreateChild<Ui::IconButton>(
- parent.get(),
- st::infoMembersAddMember);
- add->showOn(CanAddMemberValue(channel));
- add->addClickHandler([=] {
- Window::PeerMenuAddChannelMembers(navigation, channel);
- });
- parent->widthValue(
- ) | rpl::start_with_next([add](int newWidth) {
- auto availableWidth = newWidth
- - st::infoMembersButtonPosition.x();
- add->moveToLeft(
- availableWidth - add->width(),
- st::infoMembersButtonPosition.y(),
- newWidth);
- }, add->lifetime());
- }
- object_ptr<Ui::RpWidget> SetupChannelMembersAndManage(
- not_null<Controller*> controller,
- not_null<Ui::RpWidget*> parent,
- not_null<PeerData*> peer) {
- using namespace rpl::mappers;
- auto channel = peer->asChannel();
- if (!channel || channel->isMegagroup()) {
- return { nullptr };
- }
- auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- parent,
- object_ptr<Ui::VerticalLayout>(parent));
- result->entity()->add(object_ptr<Ui::BoxContentDivider>(result));
- result->entity()->add(CreateSkipWidget(result));
- auto membersShown = rpl::combine(
- MembersCountValue(channel),
- Data::PeerFlagValue(
- channel,
- ChannelDataFlag::CanViewParticipants),
- (_1 > 0) && _2);
- auto membersText = tr::lng_chat_status_subscribers(
- lt_count_decimal,
- MembersCountValue(channel) | tr::to_count());
- auto membersCallback = [=] {
- controller->showSection(std::make_shared<Info::Memento>(
- channel,
- Section::Type::Members));
- };
- const auto membersWrap = result->entity()->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- result->entity(),
- object_ptr<Ui::VerticalLayout>(result->entity())));
- membersWrap->setDuration(
- st::infoSlideDuration
- )->toggleOn(rpl::duplicate(membersShown));
- const auto members = membersWrap->entity();
- {
- auto button = AddActionButton(
- members,
- std::move(membersText),
- rpl::single(true),
- std::move(membersCallback),
- nullptr)->entity();
- SetupAddChannelMember(controller, button, channel);
- }
- object_ptr<FloatingIcon>(
- members,
- st::infoIconMembers,
- st::infoChannelMembersIconPosition);
- auto adminsShown = peer->session().changes().peerFlagsValue(
- channel,
- Data::PeerUpdate::Flag::Rights
- ) | rpl::map([=] { return channel->canViewAdmins(); });
- auto adminsText = tr::lng_profile_administrators(
- lt_count_decimal,
- Info::Profile::MigratedOrMeValue(
- channel
- ) | rpl::map(
- Info::Profile::AdminsCountValue
- ) | rpl::flatten_latest() | tr::to_count());
- auto adminsCallback = [=] {
- ParticipantsBoxController::Start(
- controller,
- channel,
- ParticipantsBoxController::Role::Admins);
- };
- const auto adminsWrap = result->entity()->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- result->entity(),
- object_ptr<Ui::VerticalLayout>(result->entity())));
- adminsWrap->setDuration(
- st::infoSlideDuration
- )->toggleOn(rpl::duplicate(adminsShown));
- const auto admins = adminsWrap->entity();
- AddActionButton(
- admins,
- std::move(adminsText),
- rpl::single(true),
- std::move(adminsCallback),
- nullptr);
- object_ptr<FloatingIcon>(
- admins,
- st::menuIconAdmin,
- st::infoChannelAdminsIconPosition);
- if (EditPeerInfoBox::Available(channel)) {
- const auto sessionController = controller->parentController();
- const auto button = AddActionButton(
- result->entity(),
- tr::lng_profile_manage(),
- rpl::single(true),
- [=] { sessionController->showEditPeerBox(channel); },
- nullptr);
- object_ptr<FloatingIcon>(
- button,
- st::menuIconManage,
- st::infoChannelAdminsIconPosition);
- }
- result->setDuration(st::infoSlideDuration)->toggleOn(
- rpl::combine(
- std::move(membersShown),
- std::move(adminsShown)
- ) | rpl::map(rpl::mappers::_1 || rpl::mappers::_2));
- result->entity()->add(CreateSkipWidget(result));
- return result;
- }
- Cover *AddCover(
- not_null<Ui::VerticalLayout*> container,
- not_null<Controller*> controller,
- not_null<PeerData*> peer,
- Data::ForumTopic *topic) {
- const auto result = topic
- ? container->add(object_ptr<Cover>(
- container,
- controller->parentController(),
- topic))
- : container->add(object_ptr<Cover>(
- container,
- controller->parentController(),
- peer,
- [=] { return controller->wrapWidget(); }));
- result->showSection(
- ) | rpl::start_with_next([=](Section section) {
- controller->showSection(topic
- ? std::make_shared<Info::Memento>(topic, section)
- : std::make_shared<Info::Memento>(peer, section));
- }, result->lifetime());
- result->setOnlineCount(rpl::single(0));
- return result;
- }
- void AddDetails(
- not_null<Ui::VerticalLayout*> container,
- not_null<Controller*> controller,
- not_null<PeerData*> peer,
- Data::ForumTopic *topic,
- Origin origin) {
- if (topic) {
- container->add(SetupDetails(controller, container, topic));
- } else {
- container->add(SetupDetails(controller, container, peer, origin));
- }
- }
- } // namespace Profile
- } // namespace Info
|