From 638d4d63c5f897b5f6761a9bf0e3ea40519aa016 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 Jul 2020 18:19:25 +0400 Subject: [PATCH] Update API scheme to layer 115. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 1 + Telegram/Resources/tl/api.tl | 12 +- Telegram/SourceFiles/api/api_chat_invite.cpp | 230 ++++++++++++++++++ Telegram/SourceFiles/api/api_chat_invite.h | 74 ++++++ Telegram/SourceFiles/apiwrap.cpp | 5 +- Telegram/SourceFiles/boxes/boxes.style | 1 - Telegram/SourceFiles/boxes/confirm_box.cpp | 140 ----------- Telegram/SourceFiles/boxes/confirm_box.h | 40 --- .../SourceFiles/core/local_url_handlers.cpp | 27 +- Telegram/SourceFiles/data/data_channel.cpp | 35 +++ Telegram/SourceFiles/data/data_channel.h | 12 + Telegram/SourceFiles/data/data_peer.cpp | 5 + Telegram/SourceFiles/data/data_session.cpp | 16 +- .../SourceFiles/history/history_widget.cpp | 4 + .../window/window_session_controller.cpp | 29 ++- .../window/window_session_controller.h | 5 + 17 files changed, 414 insertions(+), 224 deletions(-) create mode 100644 Telegram/SourceFiles/api/api_chat_invite.cpp create mode 100644 Telegram/SourceFiles/api/api_chat_invite.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 6bd362bb47..0226025620 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -190,6 +190,8 @@ PRIVATE api/api_bot.h api/api_chat_filters.cpp api/api_chat_filters.h + api/api_chat_invite.cpp + api/api_chat_invite.h api/api_common.h api/api_editing.cpp api/api_editing.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d104449975..e54cbb6fff 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1132,6 +1132,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_members#one" = "{count} member, among them:"; "lng_group_invite_members#other" = "{count} members, among them:"; +"lng_channel_invite_private" = "This channel is private.\nPlease join it to continue viewing its content."; "lng_group_invite_create" = "Create an invite link"; "lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index c381558ba3..6a59cb81d6 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -62,10 +62,9 @@ inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector< inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia; inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia; inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia; -inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; +inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia; inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia; -inputMediaGifExternal#4843b0fd url:string q:string = InputMedia; inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia; @@ -529,6 +528,7 @@ chatInviteExported#fc2e05bc link:string = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; +chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite; inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; @@ -619,11 +619,6 @@ channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector min_age_confirm:flags.1?int = help.TermsOfService; -foundGif#162ecc1f url:string thumb_url:string content_url:string content_type:string w:int h:int = FoundGif; -foundGifCached#9c750409 url:string photo:Photo document:Document = FoundGif; - -messages.foundGifs#450a1c0a next_offset:int results:Vector = messages.FoundGifs; - messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs; messages.savedGifs#2e0709a5 hash:int gifs:Vector = messages.SavedGifs; @@ -1312,7 +1307,6 @@ messages.migrateChat#15a3b8e3 chat_id:int = Updates; messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector = Bool; messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document; -messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; @@ -1502,4 +1496,4 @@ folders.deleteFolder#1c295881 folder_id:int = Updates; stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats; stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph; -// LAYER 114 +// LAYER 115 diff --git a/Telegram/SourceFiles/api/api_chat_invite.cpp b/Telegram/SourceFiles/api/api_chat_invite.cpp new file mode 100644 index 0000000000..020f2775c0 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_invite.cpp @@ -0,0 +1,230 @@ +/* +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 "api/api_chat_invite.h" + +#include "apiwrap.h" +#include "window/window_session_controller.h" +#include "main/main_session.h" +#include "ui/empty_userpic.h" +#include "core/application.h" +#include "data/data_session.h" +#include "data/data_photo.h" +#include "data/data_photo_media.h" +#include "data/data_channel.h" +#include "data/data_user.h" +#include "data/data_file_origin.h" +#include "boxes/confirm_box.h" +#include "boxes/abstract_box.h" +#include "styles/style_boxes.h" +#include "styles/style_layers.h" + +namespace Api { + +void CheckChatInvite( + not_null controller, + const QString &hash, + ChannelData *invitePeekChannel) { + const auto session = &controller->session(); + const auto weak = base::make_weak(controller.get()); + session->api().checkChatInvite(hash, [=](const MTPChatInvite &result) { + Core::App().hideMediaView(); + result.match([=](const MTPDchatInvite &data) { + const auto box = Ui::show(Box( + session, + data, + invitePeekChannel, + [=] { session->api().importChatInvite(hash); })); + if (invitePeekChannel) { + box->boxClosing( + ) | rpl::filter([=] { + return !invitePeekChannel->amIn(); + }) | rpl::start_with_next([=] { + if (const auto strong = weak.get()) { + strong->clearSectionStack(Window::SectionShow( + Window::SectionShow::Way::ClearStack, + anim::type::normal, + anim::activation::background)); + } + }, box->lifetime()); + } + }, [=](const MTPDchatInviteAlready &data) { + if (const auto chat = session->data().processChat(data.vchat())) { + if (const auto channel = chat->asChannel()) { + channel->clearInvitePeek(); + } + if (const auto strong = weak.get()) { + strong->showPeerHistory( + chat, + Window::SectionShow::Way::Forward); + } + } + }, [=](const MTPDchatInvitePeek &data) { + if (const auto chat = session->data().processChat(data.vchat())) { + if (const auto channel = chat->asChannel()) { + channel->setInvitePeek(hash, data.vexpires().v); + if (const auto strong = weak.get()) { + strong->showPeerHistory( + chat, + Window::SectionShow::Way::Forward); + } + } + } + }); + }, [=](const RPCError &error) { + if (error.code() != 400) { + return; + } + Core::App().hideMediaView(); + Ui::show(Box(tr::lng_group_invite_bad_link(tr::now))); + }); +} + +} // namespace Api + +ConfirmInviteBox::ConfirmInviteBox( + QWidget*, + not_null session, + const MTPDchatInvite &data, + ChannelData *invitePeekChannel, + Fn submit) +: _session(session) +, _submit(std::move(submit)) +, _title(this, st::confirmInviteTitle) +, _status(this, st::confirmInviteStatus) +, _participants(GetParticipants(_session, data)) +, _isChannel(data.is_channel() && !data.is_megagroup()) { + const auto title = qs(data.vtitle()); + const auto count = data.vparticipants_count().v; + const auto status = [&] { + return invitePeekChannel + ? tr::lng_channel_invite_private(tr::now) + : (!_participants.empty() && _participants.size() < count) + ? tr::lng_group_invite_members(tr::now, lt_count, count) + : (count > 0) + ? tr::lng_chat_status_members(tr::now, lt_count_decimal, count) + : _isChannel + ? tr::lng_channel_status(tr::now) + : tr::lng_group_status(tr::now); + }(); + _title->setText(title); + _status->setText(status); + + const auto photo = _session->data().processPhoto(data.vphoto()); + if (!photo->isNull()) { + _photo = photo->createMediaView(); + _photo->wanted(Data::PhotoSize::Small, Data::FileOrigin()); + if (!_photo->image(Data::PhotoSize::Small)) { + _session->downloaderTaskFinished( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); + } + } else { + _photoEmpty = std::make_unique( + Data::PeerUserpicColor(0), + title); + } +} + +ConfirmInviteBox::~ConfirmInviteBox() = default; + +auto ConfirmInviteBox::GetParticipants( + not_null session, + const MTPDchatInvite &data) +-> std::vector { + const auto participants = data.vparticipants(); + if (!participants) { + return {}; + } + const auto &v = participants->v; + auto result = std::vector(); + result.reserve(v.size()); + for (const auto &participant : v) { + if (const auto user = session->data().processUser(participant)) { + result.push_back(Participant{ user }); + } + } + return result; +} + +void ConfirmInviteBox::prepare() { + addButton( + (_isChannel + ? tr::lng_profile_join_channel() + : tr::lng_profile_join_group()), + _submit); + addButton(tr::lng_cancel(), [=] { closeBox(); }); + + while (_participants.size() > 4) { + _participants.pop_back(); + } + + auto newHeight = st::confirmInviteStatusTop + _status->height() + st::boxPadding.bottom(); + if (!_participants.empty()) { + int skip = (st::boxWideWidth - 4 * st::confirmInviteUserPhotoSize) / 5; + int padding = skip / 2; + _userWidth = (st::confirmInviteUserPhotoSize + 2 * padding); + int sumWidth = _participants.size() * _userWidth; + int left = (st::boxWideWidth - sumWidth) / 2; + for (const auto &participant : _participants) { + auto name = new Ui::FlatLabel(this, st::confirmInviteUserName); + name->resizeToWidth(st::confirmInviteUserPhotoSize + padding); + name->setText(participant.user->firstName.isEmpty() + ? participant.user->name + : participant.user->firstName); + name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop); + left += _userWidth; + } + + newHeight += st::confirmInviteUserHeight; + } + setDimensions(st::boxWideWidth, newHeight); +} + +void ConfirmInviteBox::resizeEvent(QResizeEvent *e) { + BoxContent::resizeEvent(e); + _title->move((width() - _title->width()) / 2, st::confirmInviteTitleTop); + _status->move((width() - _status->width()) / 2, st::confirmInviteStatusTop); +} + +void ConfirmInviteBox::paintEvent(QPaintEvent *e) { + BoxContent::paintEvent(e); + + Painter p(this); + + if (_photo) { + if (const auto image = _photo->image(Data::PhotoSize::Small)) { + p.drawPixmap( + (width() - st::confirmInvitePhotoSize) / 2, + st::confirmInvitePhotoTop, + image->pixCircled( + st::confirmInvitePhotoSize, + st::confirmInvitePhotoSize)); + } + } else if (_photoEmpty) { + _photoEmpty->paint( + p, + (width() - st::confirmInvitePhotoSize) / 2, + st::confirmInvitePhotoTop, + width(), + st::confirmInvitePhotoSize); + } + + int sumWidth = _participants.size() * _userWidth; + int left = (width() - sumWidth) / 2; + for (auto &participant : _participants) { + participant.user->paintUserpicLeft( + p, + participant.userpic, + left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, + st::confirmInviteUserPhotoTop, + width(), + st::confirmInviteUserPhotoSize); + left += _userWidth; + } +} diff --git a/Telegram/SourceFiles/api/api_chat_invite.h b/Telegram/SourceFiles/api/api_chat_invite.h new file mode 100644 index 0000000000..be59127614 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_invite.h @@ -0,0 +1,74 @@ +/* +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 +*/ +#pragma once + +#include "ui/layers/box_content.h" + +class UserData; +class ChannelData; + +namespace Window { +class SessionController; +} // namespace Window + +namespace Data { +class CloudImageView; +class PhotoMedia; +} // namespace Data + +namespace Ui { +class EmptyUserpic; +} // namespace Ui + +namespace Api { + +void CheckChatInvite( + not_null controller, + const QString &hash, + ChannelData *invitePeekChannel = nullptr); + +} // namespace Api + +class ConfirmInviteBox final : public Ui::BoxContent { +public: + ConfirmInviteBox( + QWidget*, + not_null session, + const MTPDchatInvite &data, + ChannelData *invitePeekChannel, + Fn submit); + ~ConfirmInviteBox(); + +protected: + void prepare() override; + + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + +private: + struct Participant { + not_null user; + std::shared_ptr userpic; + }; + static std::vector GetParticipants( + not_null session, + const MTPDchatInvite &data); + + const not_null _session; + + Fn _submit; + object_ptr _title; + object_ptr _status; + std::shared_ptr _photo; + std::unique_ptr _photoEmpty; + std::vector _participants; + bool _isChannel = false; + + int _userWidth = 0; + +}; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 6e0c19254c..f4284c0300 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1645,7 +1645,7 @@ void ApiWrap::requestSelfParticipant(not_null channel) { }).fail([=](const RPCError &error) { _selfParticipantRequests.erase(channel); if (error.type() == qstr("CHANNEL_PRIVATE")) { - channel->markForbidden(); + channel->privateErrorReceived(); } finalize(-1, 0); }).afterDelay(kSmallDelayMs).send(); @@ -1962,6 +1962,9 @@ void ApiWrap::joinChannel(not_null channel) { applyUpdates(result); }).fail([=](const RPCError &error) { if (error.type() == qstr("CHANNEL_PRIVATE") + && channel->invitePeekExpires()) { + channel->privateErrorReceived(); + } else if (error.type() == qstr("CHANNEL_PRIVATE") || error.type() == qstr("CHANNEL_PUBLIC_GROUP_NA") || error.type() == qstr("USER_BANNED_IN_CHANNEL")) { Ui::show(Box(channel->isMegagroup() diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 50b45dbf98..e166773021 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -94,7 +94,6 @@ confirmInviteTitle: FlatLabel(defaultFlatLabel) { confirmInviteStatus: FlatLabel(boxLabel) { align: align(center); minWidth: 320px; - maxHeight: 20px; textFg: windowSubTextFg; } confirmInviteTitleTop: 106px; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index d0964956db..afccdd719b 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -861,146 +861,6 @@ void DeleteMessagesBox::deleteAndClear() { session->data().sendHistoryChangeNotifications(); } -ConfirmInviteBox::ConfirmInviteBox( - QWidget*, - not_null session, - const MTPDchatInvite &data, - Fn submit) -: _session(session) -, _submit(std::move(submit)) -, _title(this, st::confirmInviteTitle) -, _status(this, st::confirmInviteStatus) -, _participants(GetParticipants(_session, data)) -, _isChannel(data.is_channel() && !data.is_megagroup()) { - const auto title = qs(data.vtitle()); - const auto count = data.vparticipants_count().v; - const auto status = [&] { - return (!_participants.empty() && _participants.size() < count) - ? tr::lng_group_invite_members(tr::now, lt_count, count) - : (count > 0) - ? tr::lng_chat_status_members(tr::now, lt_count_decimal, count) - : _isChannel - ? tr::lng_channel_status(tr::now) - : tr::lng_group_status(tr::now); - }(); - _title->setText(title); - _status->setText(status); - - const auto photo = _session->data().processPhoto(data.vphoto()); - if (!photo->isNull()) { - _photo = photo->createMediaView(); - _photo->wanted(Data::PhotoSize::Small, Data::FileOrigin()); - if (!_photo->image(Data::PhotoSize::Small)) { - _session->downloaderTaskFinished( - ) | rpl::start_with_next([=] { - update(); - }, lifetime()); - } - } else { - _photoEmpty = std::make_unique( - Data::PeerUserpicColor(0), - title); - } -} - -auto ConfirmInviteBox::GetParticipants( - not_null session, - const MTPDchatInvite &data) --> std::vector { - const auto participants = data.vparticipants(); - if (!participants) { - return {}; - } - const auto &v = participants->v; - auto result = std::vector(); - result.reserve(v.size()); - for (const auto &participant : v) { - if (const auto user = session->data().processUser(participant)) { - result.push_back(Participant{ user }); - } - } - return result; -} - -void ConfirmInviteBox::prepare() { - addButton( - (_isChannel - ? tr::lng_profile_join_channel() - : tr::lng_profile_join_group()), - _submit); - addButton(tr::lng_cancel(), [=] { closeBox(); }); - - while (_participants.size() > 4) { - _participants.pop_back(); - } - - auto newHeight = st::confirmInviteStatusTop + _status->height() + st::boxPadding.bottom(); - if (!_participants.empty()) { - int skip = (st::boxWideWidth - 4 * st::confirmInviteUserPhotoSize) / 5; - int padding = skip / 2; - _userWidth = (st::confirmInviteUserPhotoSize + 2 * padding); - int sumWidth = _participants.size() * _userWidth; - int left = (st::boxWideWidth - sumWidth) / 2; - for (const auto &participant : _participants) { - auto name = new Ui::FlatLabel(this, st::confirmInviteUserName); - name->resizeToWidth(st::confirmInviteUserPhotoSize + padding); - name->setText(participant.user->firstName.isEmpty() - ? participant.user->name - : participant.user->firstName); - name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop); - left += _userWidth; - } - - newHeight += st::confirmInviteUserHeight; - } - setDimensions(st::boxWideWidth, newHeight); -} - -void ConfirmInviteBox::resizeEvent(QResizeEvent *e) { - BoxContent::resizeEvent(e); - _title->move((width() - _title->width()) / 2, st::confirmInviteTitleTop); - _status->move((width() - _status->width()) / 2, st::confirmInviteStatusTop); -} - -void ConfirmInviteBox::paintEvent(QPaintEvent *e) { - BoxContent::paintEvent(e); - - Painter p(this); - - if (_photo) { - if (const auto image = _photo->image(Data::PhotoSize::Small)) { - p.drawPixmap( - (width() - st::confirmInvitePhotoSize) / 2, - st::confirmInvitePhotoTop, - image->pixCircled( - st::confirmInvitePhotoSize, - st::confirmInvitePhotoSize)); - } - } else if (_photoEmpty) { - _photoEmpty->paint( - p, - (width() - st::confirmInvitePhotoSize) / 2, - st::confirmInvitePhotoTop, - width(), - st::confirmInvitePhotoSize); - } - - int sumWidth = _participants.size() * _userWidth; - int left = (width() - sumWidth) / 2; - for (auto &participant : _participants) { - participant.user->paintUserpicLeft( - p, - participant.userpic, - left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, - st::confirmInviteUserPhotoTop, - width(), - st::confirmInviteUserPhotoSize); - left += _userWidth; - } -} - -ConfirmInviteBox::~ConfirmInviteBox() = default; - ConfirmDontWarnBox::ConfirmDontWarnBox( QWidget*, rpl::producer text, diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index 91a0e0b20f..bfa9ca13ac 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -206,46 +206,6 @@ private: }; -class ConfirmInviteBox final - : public Ui::BoxContent - , private base::Subscriber { -public: - ConfirmInviteBox( - QWidget*, - not_null session, - const MTPDchatInvite &data, - Fn submit); - ~ConfirmInviteBox(); - -protected: - void prepare() override; - - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - -private: - struct Participant { - not_null user; - std::shared_ptr userpic; - }; - static std::vector GetParticipants( - not_null session, - const MTPDchatInvite &data); - - const not_null _session; - - Fn _submit; - object_ptr _title; - object_ptr _status; - std::shared_ptr _photo; - std::unique_ptr _photoEmpty; - std::vector _participants; - bool _isChannel = false; - - int _userWidth = 0; - -}; - class ConfirmDontWarnBox : public Ui::BoxContent { public: ConfirmDontWarnBox( diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index d70f75e4a5..5563db8b26 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/local_url_handlers.h" #include "api/api_text_entities.h" +#include "api/api_chat_invite.h" #include "base/qthelp_regex.h" #include "base/qthelp_url.h" #include "lang/lang_cloud_manager.h" @@ -50,31 +51,7 @@ bool JoinGroupByHash( if (!controller) { return false; } - const auto hash = match->captured(1); - const auto session = &controller->session(); - const auto weak = base::make_weak(controller); - session->api().checkChatInvite(hash, [=](const MTPChatInvite &result) { - Core::App().hideMediaView(); - result.match([=](const MTPDchatInvite &data) { - Ui::show(Box(session, data, [=] { - session->api().importChatInvite(hash); - })); - }, [=](const MTPDchatInviteAlready &data) { - if (const auto chat = session->data().processChat(data.vchat())) { - if (const auto strong = weak.get()) { - strong->showPeerHistory( - chat, - Window::SectionShow::Way::Forward); - } - } - }); - }, [=](const RPCError &error) { - if (error.code() != 400) { - return; - } - Core::App().hideMediaView(); - Ui::show(Box(tr::lng_group_invite_bad_link(tr::now))); - }); + Api::CheckChatInvite(controller, match->captured(1)); return true; } diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 0feaa38873..f5d4a3cbcf 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "history/history.h" #include "main/main_session.h" +#include "api/api_chat_invite.h" #include "apiwrap.h" namespace { @@ -632,6 +633,40 @@ void ChannelData::growSlowmodeLastMessage(TimeId when) { session().changes().peerUpdated(this, UpdateFlag::Slowmode); } +void ChannelData::setInvitePeek(const QString &hash, TimeId expires) { + if (!_invitePeek) { + _invitePeek = std::make_unique(); + } + _invitePeek->hash = hash; + _invitePeek->expires = expires; +} + +void ChannelData::clearInvitePeek() { + _invitePeek = nullptr; +} + +TimeId ChannelData::invitePeekExpires() const { + return _invitePeek ? _invitePeek->expires : 0; +} + +QString ChannelData::invitePeekHash() const { + return _invitePeek ? _invitePeek->hash : QString(); +} + +void ChannelData::privateErrorReceived() { + if (const auto expires = invitePeekExpires()) { + const auto hash = invitePeekHash(); + for (const auto window : session().windows()) { + clearInvitePeek(); + Api::CheckChatInvite(window, hash, this); + return; + } + _invitePeek->expires = base::unixtime::now(); + } else { + markForbidden(); + } +} + namespace Data { void ApplyMigration( diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 4816ba5cf9..288f13dd3a 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -386,6 +386,12 @@ public: [[nodiscard]] TimeId slowmodeLastMessage() const; void growSlowmodeLastMessage(TimeId when); + void setInvitePeek(const QString &hash, TimeId expires); + void clearInvitePeek(); + [[nodiscard]] TimeId invitePeekExpires() const; + [[nodiscard]] QString invitePeekHash() const; + void privateErrorReceived(); + // Still public data members. uint64 access = 0; @@ -401,6 +407,11 @@ public: TimeId inviteDate = 0; private: + struct InvitePeek { + QString hash; + TimeId expires = 0; + }; + auto unavailableReasons() const -> const std::vector & override; bool canEditLastAdmin(not_null user) const; @@ -423,6 +434,7 @@ private: TimeId _restrictedUntil; std::vector _unavailableReasons; + std::unique_ptr _invitePeek; QString _inviteLink; ChannelData *_linkedChat = nullptr; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 3c8ed5e1b4..754edfc0fb 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -475,6 +475,11 @@ void PeerData::setPinnedMessageId(MsgId messageId) { } bool PeerData::canExportChatHistory() const { + if (const auto channel = asChannel()) { + if (!channel->amIn() && channel->invitePeekExpires()) { + return false; + } + } for (const auto &block : _owner->history(id)->blocks) { for (const auto &message : block->messages) { if (!message->data()->serviceMsg()) { diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 0ad1ea07da..b9d21b25ae 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1008,14 +1008,16 @@ void Session::setupChannelLeavingViewer() { PeerUpdate::Flag::ChannelAmIn ) | rpl::map([](const PeerUpdate &update) { return update.peer->asChannel(); - }) | rpl::filter([](not_null channel) { - return !(channel->amIn()); }) | rpl::start_with_next([=](not_null channel) { -// channel->clearFeed(); // #feed - if (const auto history = historyLoaded(channel->id)) { - history->removeJoinedMessage(); - history->updateChatListExistence(); - history->updateChatListSortPosition(); + if (channel->amIn()) { + channel->clearInvitePeek(); + } else { +// channel->clearFeed(); // #feed + if (const auto history = historyLoaded(channel->id)) { + history->removeJoinedMessage(); + history->updateChatListExistence(); + history->updateChatListSortPosition(); + } } }, _lifetime); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 283a6c8a2f..a36a76b377 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2391,6 +2391,10 @@ void HistoryWidget::unreadCountUpdated() { void HistoryWidget::messagesFailed(const RPCError &error, int requestId) { if (error.type() == qstr("CHANNEL_PRIVATE") + && _peer->isChannel() + && _peer->asChannel()->invitePeekExpires()) { + _peer->asChannel()->privateErrorReceived(); + } else if (error.type() == qstr("CHANNEL_PRIVATE") || error.type() == qstr("CHANNEL_PUBLIC_GROUP_NA") || error.type() == qstr("USER_BANNED_IN_CHANNEL")) { auto was = _peer; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 379b7f2df7..b961d2a9de 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/main_session_settings.h" #include "apiwrap.h" +#include "api/api_chat_invite.h" #include "support/support_helper.h" #include "facades.h" #include "styles/style_window.h" @@ -133,7 +134,8 @@ SessionController::SessionController( , _tabbedSelector( std::make_unique( _window->widget(), - this)) { + this)) +, _invitePeekTimer([=] { checkInvitePeek(); }) { init(); if (Media::Player::instance()->pauseGifByRoundVideo()) { @@ -309,6 +311,7 @@ void SessionController::setActiveChatEntry(Dialogs::RowDescriptor row) { const auto now = row.key.history(); if (was && was != now) { was->setFakeUnreadWhileOpened(false); + _invitePeekTimer.cancel(); } _activeChatEntry = row; if (now) { @@ -317,6 +320,30 @@ void SessionController::setActiveChatEntry(Dialogs::RowDescriptor row) { if (session().supportMode()) { pushToChatEntryHistory(row); } + checkInvitePeek(); +} + +void SessionController::checkInvitePeek() { + const auto history = activeChatCurrent().history(); + if (!history) { + return; + } + const auto channel = history->peer->asChannel(); + if (!channel) { + return; + } + const auto expires = channel->invitePeekExpires(); + if (!expires) { + return; + } + const auto now = base::unixtime::now(); + if (expires > now) { + _invitePeekTimer.callOnce((expires - now) * crl::time(1000)); + return; + } + const auto hash = channel->invitePeekHash(); + channel->clearInvitePeek(); + Api::CheckChatInvite(this, hash, channel); } void SessionController::resetFakeUnreadWhileOpened() { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 4ba1a51e14..63c4d47ab3 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/observer.h" #include "base/object_ptr.h" #include "base/weak_ptr.h" +#include "base/timer.h" #include "dialogs/dialogs_key.h" #include "ui/effects/animation_value.h" @@ -324,6 +325,8 @@ private: bool chatEntryHistoryMove(int steps); void resetFakeUnreadWhileOpened(); + void checkInvitePeek(); + const not_null _window; std::unique_ptr _passportForm; @@ -342,6 +345,8 @@ private: int _chatEntryHistoryPosition = -1; bool _selectingPeer = false; + base::Timer _invitePeekTimer; + rpl::variable _activeChatsFilter; PeerData *_showEditPeer = nullptr;