diff --git a/.gitignore b/.gitignore index 35282e1e49..f4fe857af0 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,14 @@ stage *.*~ .idea/ cmake-build-debug/ + +# Local configuration files +settings.local.json +*.local.json +.env +.env.local +.env.*.local + +# Cursor IDE local settings (but keep .cursor/rules/) +.cursor/* +!.cursor/rules/ diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index abb43c7e5b..308a9cfb43 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -1945,6 +1945,35 @@ void Stories::albumRename( }).send(); } +void Stories::albumDelete(not_null peer, int id) { + _owner->session().api().request(MTPstories_DeleteAlbum( + peer->input, + MTP_int(id) + )).send(); + + auto &albums = _albums[peer->id]; + auto current = albums.list.current(); + current.erase( + ranges::remove(current, id, &StoryAlbum::id), + end(current)); + albums.list = std::move(current); + + const auto i = albums.sets.find(id); + if (i != end(albums.sets)) { + _owner->session().api().request( + base::take(i->second.requestId)).cancel(); + for (const auto storyId : i->second.albumKnownInArchive) { + if (const auto story = lookup({ peer->id, storyId })) { + auto now = (*story)->albumIds(); + if (now.remove(id)) { + (*story)->setAlbumIds(std::move(now)); + } + } + } + albums.sets.erase(i); + } +} + void Stories::notifyAlbumUpdate(StoryAlbumUpdate &&update) { const auto peerId = update.peer->id; const auto i = _albums.find(peerId); diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index d0492cef8a..c8ea41036b 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -235,6 +235,7 @@ public: const QString &title, Fn done, Fn fail); + void albumDelete(not_null peer, int id); void notifyAlbumUpdate(StoryAlbumUpdate &&update); [[nodiscard]] rpl::producer albumUpdates() const; diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index 5d94ac5c0d..1c92ecaf0d 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -367,7 +367,12 @@ bool Story::pinnedToTop() const { } void Story::setInProfile(bool value) { - _inProfile = value; + if (_inProfile != value) { + _inProfile = value; + if (const auto item = _peer->owner().stories().lookupItem(this)) { + item->setStoryInProfile(value); + } + } } bool Story::inProfile() const { diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index d0dce0f1ea..ebc6958c41 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -356,6 +356,8 @@ enum class MessageFlag : uint64 { StarsPaidSuggested = (1ULL << 52), TonPaidSuggested = (1ULL << 53), + + StoryInProfile = (1ULL << 54), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index c5587b25d5..5f5424254b 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1634,6 +1634,17 @@ void HistoryItem::setIsPinned(bool pinned) { } } +void HistoryItem::setStoryInProfile(bool inProfile) { + if (storyInProfile() == inProfile) { + return; + } else if (inProfile) { + _flags |= MessageFlag::StoryInProfile; + } else { + _flags &= ~MessageFlag::StoryInProfile; + } + _history->owner().notifyItemDataChange(this); +} + void HistoryItem::returnSavedMedia() { if (!isEditingMedia()) { return; @@ -2014,6 +2025,11 @@ void HistoryItem::setStoryFields(not_null story) { } else { _flags &= ~MessageFlag::Pinned; } + if (story->inProfile()) { + _flags |= MessageFlag::StoryInProfile; + } else { + _flags &= ~MessageFlag::StoryInProfile; + } } void HistoryItem::applyEdition(const MTPDmessageService &message) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 978624e85e..1f65b0c6a8 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -235,6 +235,9 @@ public: [[nodiscard]] bool invertMedia() const { return _flags & MessageFlag::InvertMedia; } + [[nodiscard]] bool storyInProfile() const { + return _flags & MessageFlag::StoryInProfile; + } [[nodiscard]] bool unread(not_null thread) const; [[nodiscard]] bool showNotification() const; void markClientSideAsRead(); @@ -250,6 +253,7 @@ public: void markMediaAndMentionRead(); bool markContentsRead(bool fromThisClient = false); void setIsPinned(bool isPinned); + void setStoryInProfile(bool inProfile); // For edit media in history_message. void returnSavedMedia(); diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 8e731fc914..71bac1b3c8 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -588,10 +588,6 @@ void TopBar::setStories(rpl::producer content) { updateControlsVisibility(anim::type::instant); } -void TopBar::setStoriesArchive(bool archive) { - _storiesArchive = archive; -} - void TopBar::setSelectedItems(SelectedItems &&items) { auto wasSelectionMode = selectionMode(); _selectedItems = std::move(items); @@ -628,10 +624,19 @@ void TopBar::updateSelectionState() { _canDelete = computeCanDelete(); _canForward = computeCanForward(); _canUnpinStories = computeCanUnpinStories(); + _canToggleStoryPin = computeCanToggleStoryPin(); + _allStoriesInProfile = computeAllStoriesInProfile(); _selectionText->entity()->setValue(generateSelectedText()); _delete->toggle(_canDelete, anim::type::instant); _forward->toggle(_canForward, anim::type::instant); _toggleStoryInProfile->toggle(_canToggleStoryPin, anim::type::instant); + _toggleStoryInProfile->entity()->setIconOverride( + (_allStoriesInProfile + ? &_st.storiesArchive.icon + : &_st.storiesSave.icon), + (_allStoriesInProfile + ? &_st.storiesArchive.iconOver + : &_st.storiesSave.iconOver)); _toggleStoryPin->toggle(_canToggleStoryPin, anim::type::instant); _toggleStoryPin->entity()->setIconOverride( _canUnpinStories ? &_st.storiesUnpin.icon : nullptr, @@ -652,6 +657,7 @@ void TopBar::createSelectionControls() { _canForward = computeCanForward(); _canUnpinStories = computeCanUnpinStories(); _canToggleStoryPin = computeCanToggleStoryPin(); + _allStoriesInProfile = computeAllStoriesInProfile(); _cancelSelection = wrap(Ui::CreateChild>( this, object_ptr(this, _st.mediaCancel), @@ -710,16 +716,18 @@ void TopBar::createSelectionControls() { this, object_ptr( this, - _storiesArchive ? _st.storiesSave : _st.storiesArchive), + _allStoriesInProfile ? _st.storiesArchive : _st.storiesSave), st::infoTopBarScale)); registerToggleControlCallback( _toggleStoryInProfile.data(), [this] { return selectionMode() && _canToggleStoryPin; }); _toggleStoryInProfile->setDuration(st::infoTopBarDuration); _toggleStoryInProfile->entity()->clicks( - ) | rpl::map_to( - SelectionAction::ToggleStoryInProfile - ) | rpl::start_to_stream( + ) | rpl::map([=] { + return _allStoriesInProfile + ? SelectionAction::ToggleStoryToArchive + : SelectionAction::ToggleStoryToProfile; + }) | rpl::start_to_stream( _selectionActionRequests, _cancelSelection->lifetime()); _toggleStoryInProfile->entity()->setVisible(_canToggleStoryPin); @@ -769,6 +777,12 @@ bool TopBar::computeCanToggleStoryPin() const { &SelectedItem::canToggleStoryPin); } +bool TopBar::computeAllStoriesInProfile() const { + return ranges::all_of( + _selectedItems.list, + &SelectedItem::storyInProfile); +} + Ui::StringWithNumbers TopBar::generateSelectedText() const { return _selectedItems.title(_selectedItems.list.size()); } diff --git a/Telegram/SourceFiles/info/info_top_bar.h b/Telegram/SourceFiles/info/info_top_bar.h index 71cbaa1a3d..6bd2e7ebbf 100644 --- a/Telegram/SourceFiles/info/info_top_bar.h +++ b/Telegram/SourceFiles/info/info_top_bar.h @@ -63,7 +63,6 @@ public: void setTitle(TitleDescriptor descriptor); void setStories(rpl::producer content); - void setStoriesArchive(bool archive); void enableBackButton(); void highlight(); @@ -131,6 +130,7 @@ private: [[nodiscard]] bool computeCanForward() const; [[nodiscard]] bool computeCanUnpinStories() const; [[nodiscard]] bool computeCanToggleStoryPin() const; + [[nodiscard]] bool computeAllStoriesInProfile() const; void updateSelectionState(); void createSelectionControls(); @@ -178,7 +178,7 @@ private: bool _canForward = false; bool _canToggleStoryPin = false; bool _canUnpinStories = false; - bool _storiesArchive = false; + bool _allStoriesInProfile = false; QPointer> _cancelSelection; QPointer> _selectionText; QPointer> _forward; diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 31b8aa861c..ec982a3f88 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -657,8 +657,6 @@ void WrapWidget::finishShowContent() { .subtitle = _content->subtitle(), }); _topBar->setStories(_content->titleStories()); - _topBar->setStoriesArchive( - _controller->key().storiesAlbumId() == Stories::ArchiveId()); } _desiredHeights.fire(desiredHeightForContent()); _desiredShadowVisibilities.fire(_content->desiredShadowVisibility()); @@ -787,7 +785,6 @@ bool WrapWidget::showInternal( && (params.way == Window::SectionShow::Way::ClearStack); if (_controller->validateMementoPeer(content)) { if (!skipInternal && _content->showInternal(content)) { - highlightTopBar(); return true; } } diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h index ee656f17b9..3362ec848d 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.h +++ b/Telegram/SourceFiles/info/info_wrap_widget.h @@ -65,6 +65,7 @@ struct SelectedItem { bool canForward = false; bool canToggleStoryPin = false; bool canUnpinStory = false; + bool storyInProfile = false; }; struct SelectedItems { @@ -80,7 +81,8 @@ enum class SelectionAction { Forward, Delete, ToggleStoryPin, - ToggleStoryInProfile, + ToggleStoryToProfile, + ToggleStoryToArchive, }; class WrapWidget final : public Window::SectionWidget { diff --git a/Telegram/SourceFiles/info/media/info_media_common.h b/Telegram/SourceFiles/info/media/info_media_common.h index d5a8f726d4..b8c4830f26 100644 --- a/Telegram/SourceFiles/info/media/info_media_common.h +++ b/Telegram/SourceFiles/info/media/info_media_common.h @@ -32,6 +32,7 @@ struct ListItemSelectionData { bool canForward = false; bool canToggleStoryPin = false; bool canUnpinStory = false; + bool storyInProfile = false; friend inline bool operator==( ListItemSelectionData, diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 3db2c6b11f..fd84dca000 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -58,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" #include "boxes/delete_messages_box.h" #include "boxes/peer_list_controllers.h" +#include "boxes/sticker_set_box.h" // StickerPremiumMark #include "core/file_utilities.h" #include "core/application.h" #include "ui/toast/toast.h" @@ -66,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_menu_icons.h" #include "styles/style_chat.h" +#include "styles/style_credits.h" // giftBoxHiddenMark #include #include @@ -152,7 +154,11 @@ ListWidget::ListWidget( _provider->type(), [=] { scrollDateCheck(); }, [=] { scrollDateHide(); })) -, _storiesAddToAlbumId(controller->storiesAddToAlbumId()) { +, _storiesAddToAlbumId(controller->storiesAddToAlbumId()) +, _hiddenMark(std::make_unique( + &_controller->session(), + st::giftBoxHiddenMark, + RectPart::Center)) { start(); } @@ -312,8 +318,11 @@ void ListWidget::selectionAction(SelectionAction action) { case SelectionAction::Clear: clearSelected(); return; case SelectionAction::Forward: forwardSelected(); return; case SelectionAction::Delete: deleteSelected(); return; - case SelectionAction::ToggleStoryInProfile: - toggleStoryInProfileSelected(); + case SelectionAction::ToggleStoryToProfile: + toggleStoryInProfileSelected(true); + return; + case SelectionAction::ToggleStoryToArchive: + toggleStoryInProfileSelected(false); return; case SelectionAction::ToggleStoryPin: toggleStoryPinSelected(); return; } @@ -395,6 +404,7 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems { result.canForward = selection.canForward; result.canToggleStoryPin = selection.canToggleStoryPin; result.canUnpinStory = selection.canUnpinStory; + result.storyInProfile = selection.storyInProfile; return result; }; auto transformation = [&](const auto &item) { @@ -527,6 +537,10 @@ bool ListWidget::itemVisible(not_null item) { return true; } +not_null ListWidget::hiddenMark() { + return _hiddenMark.get(); +} + QString ListWidget::tooltipText() const { if (const auto link = ClickHandler::getActive()) { return link->tooltip(); @@ -1020,6 +1034,11 @@ void ListWidget::showContextMenu( return !item.second.canToggleStoryPin; }); }; + const auto allInProfile = [&] { + return ranges::all_of(_selected, [](auto &&item) { + return item.second.storyInProfile; + }); + }; const auto canUnpinStoryAll = [&] { return ranges::any_of(_selected, [](auto &&item) { return item.second.canUnpinStory; @@ -1125,13 +1144,14 @@ void ListWidget::showContextMenu( } if (overSelected == SelectionState::OverSelectedItems) { if (canToggleStoryPinAll()) { - const auto albumId = _controller->storiesAlbumId(); - const auto toProfile = (albumId == Stories::ArchiveId()); + const auto toProfile = !allInProfile(); _contextMenu->addAction( (toProfile ? tr::lng_mediaview_save_to_profile : tr::lng_archived_add)(tr::now), - crl::guard(this, [this] { toggleStoryInProfileSelected(); }), + crl::guard(this, [=] { + toggleStoryInProfileSelected(toProfile); + }), (toProfile ? &st::menuIconStoriesSave : &st::menuIconStoriesArchive)); @@ -1177,14 +1197,15 @@ void ListWidget::showContextMenu( item, FullSelection); if (selectionData.canToggleStoryPin) { - const auto albumId = _controller->storiesAlbumId(); - const auto toProfile = (albumId == Stories::ArchiveId()); + const auto toProfile = !selectionData.storyInProfile; _contextMenu->addAction( (toProfile ? tr::lng_mediaview_save_to_profile : tr::lng_mediaview_archive_story)(tr::now), crl::guard(this, [=] { - toggleStoryInProfile({ 1, globalId.itemId }); + toggleStoryInProfile( + { 1, globalId.itemId }, + toProfile); }), (toProfile ? &st::menuIconStoriesSave @@ -1317,10 +1338,11 @@ void ListWidget::deleteSelected() { })); } -void ListWidget::toggleStoryInProfileSelected() { - toggleStoryInProfile(collectSelectedIds(), crl::guard(this, [=] { - clearSelected(); - })); +void ListWidget::toggleStoryInProfileSelected(bool toProfile) { + toggleStoryInProfile( + collectSelectedIds(), + toProfile, + crl::guard(this, [=] { clearSelected(); })); } void ListWidget::toggleStoryPinSelected() { @@ -1335,6 +1357,7 @@ void ListWidget::toggleStoryPinSelected() { void ListWidget::toggleStoryInProfile( MessageIdsList &&items, + bool toProfile, Fn confirmed) { auto list = std::vector(); for (const auto &id : items) { @@ -1347,8 +1370,6 @@ void ListWidget::toggleStoryInProfile( } const auto channel = peerIsChannel(list.front().peer); const auto count = int(list.size()); - const auto albumId = _controller->storiesAlbumId(); - const auto toProfile = (albumId == Stories::ArchiveId()); const auto controller = _controller; const auto sure = [=](Fn close) { using namespace ::Media::Stories; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h index 256d82adf5..c578d21f32 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h @@ -84,6 +84,7 @@ public: void unregisterHeavyItem(not_null item) override; void repaintItem(not_null item) override; bool itemVisible(not_null item) override; + not_null hiddenMark() override; // AbstractTooltipShower interface QString tooltipText() const override; @@ -192,11 +193,12 @@ private: void forwardItems(MessageIdsList &&items); void deleteSelected(); void toggleStoryPinSelected(); - void toggleStoryInProfileSelected(); + void toggleStoryInProfileSelected(bool toProfile); void deleteItem(GlobalMsgId globalId); void deleteItems(SelectedItems &&items, Fn confirmed = nullptr); void toggleStoryInProfile( MessageIdsList &&items, + bool toProfile, Fn confirmed = nullptr); void toggleStoryPin( MessageIdsList &&items, @@ -311,6 +313,7 @@ private: int _storiesAddToAlbumId = 0; base::flat_set _storiesInAlbum; base::flat_set _storyMsgsToMarkSelected; + std::unique_ptr _hiddenMark; base::unique_qptr _contextMenu; rpl::event_stream<> _checkForHide; diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp index 193c24f809..d205cc5c24 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp @@ -947,10 +947,8 @@ void InnerWidget::refreshAbout() { auto text = tr::lng_peer_gifts_empty_search( tr::now, Ui::Text::RichLangValue); - if (_entries->total > 0) { - text.append("\n\n").append(Ui::Text::Link( - tr::lng_peer_gifts_view_all(tr::now))); - } + text.append("\n\n").append(Ui::Text::Link( + tr::lng_peer_gifts_view_all(tr::now))); auto about = std::make_unique( this, rpl::single(text), diff --git a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp index 3af67c42e9..c47a5968fb 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp @@ -552,17 +552,18 @@ void InnerWidget::setupEmpty() { ), _list->heightValue() ) | rpl::start_with_next([=](auto, int listHeight) { - if (listHeight) { - _empty.destroy(); - return; + const auto padding = st::infoMediaMargin; + if (const auto raw = _empty.release()) { + raw->hide(); + raw->deleteLater(); + } + if (listHeight <= padding.bottom() + padding.top()) { + refreshEmpty(); } - refreshEmpty(); }, _list->lifetime()); } void InnerWidget::refreshEmpty() { - _empty.destroy(); - const auto albumId = _albumId.current(); const auto stories = &_controller->session().data().stories(); const auto knownEmpty = stories->albumIdsCountKnown(_peer->id, albumId); @@ -606,7 +607,6 @@ void InnerWidget::refreshEmpty() { button->setClickedCallback([=] { editAlbumStories(albumId); }); - empty->show(); _empty = std::move(empty); } else { @@ -790,10 +790,11 @@ void InnerWidget::editAlbumName(int id) { void InnerWidget::confirmDeleteAlbum(int id) { const auto done = [=](Fn close) { - _controller->session().api().request( - MTPstories_DeleteAlbum(_peer->input, MTP_int(id)) - ).send(); albumRemoved(id); + + const auto stories = &_controller->session().data().stories(); + stories->albumDelete(_peer, id); + close(); }; _controller->uiShow()->show(Ui::MakeConfirmBox({ @@ -807,7 +808,7 @@ void InnerWidget::confirmDeleteAlbum(int id) { void InnerWidget::albumAdded(Data::StoryAlbum result) { Expects(ranges::contains(_albums, result.id, &Data::StoryAlbum::id)); - _albumId = result.id; + _albumIdChanges.fire_copy(result.id); } void InnerWidget::albumRenamed(int id, QString name) { @@ -821,20 +822,8 @@ void InnerWidget::albumRenamed(int id, QString name) { void InnerWidget::albumRemoved(int id) { auto now = _albumId.current(); if (now == id) { - _albumId = 0; + _albumIdChanges.fire_copy(0); } - //const auto removeFrom = [&](Entries &entries) { - // for (auto &entry : entries.list) { - // entry.gift.collectionIds.erase( - // ranges::remove(entry.gift.collectionIds, id), - // end(entry.gift.collectionIds)); - // } - //}; - //removeFrom(_all); - //for (auto &[_, entries] : _perCollection) { - // removeFrom(entries); - //} - const auto i = ranges::find(_albums, id, &Data::StoryAlbum::id); if (i != end(_albums)) { _albums.erase(i); @@ -908,14 +897,6 @@ int InnerWidget::recountHeight() { } void InnerWidget::setScrollHeightValue(rpl::producer value) { - //using namespace rpl::mappers; - //_empty->setFullHeight(rpl::combine( - // std::move(value), - // _listTops.events_starting_with( - // _list->topValue() - // ) | rpl::flatten_latest(), - // _topHeight.value(), - // _1 - _2 + _3)); } rpl::producer InnerWidget::scrollToRequests() const { diff --git a/Telegram/SourceFiles/info/stories/info_stories_provider.cpp b/Telegram/SourceFiles/info/stories/info_stories_provider.cpp index a0d0d0ec98..844ece18a0 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_provider.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_provider.cpp @@ -343,10 +343,20 @@ std::unique_ptr Provider::createLayout( } return nullptr; }; + + const auto peer = item->history()->peer; + const auto channel = peer->asChannel(); + const auto showPinned = (_albumId == Data::kStoriesAlbumIdSaved); + const auto showHidden = peer->isSelf() + || (channel && channel->canEditStories()); + using namespace Overview::Layout; const auto options = MediaOptions{ - .pinned = item->isPinned(), .story = true, + .storyPinned = showPinned && item->isPinned(), + .storyShowPinned = showPinned, + .storyHidden = showHidden && !item->storyInProfile(), + .storyShowHidden = showHidden, }; if (const auto photo = getPhoto()) { return std::make_unique(delegate, item, photo, options); @@ -379,6 +389,7 @@ ListItemSelectionData Provider::computeSelectionData( result.canForward = peer->isSelf() && story->canShare(); result.canDelete = story->canDelete(); result.canUnpinStory = story->pinnedToTop(); + result.storyInProfile = story->inProfile(); } result.canToggleStoryPin = peer->isSelf() || (channel && channel->canEditStories()); diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index a537000a05..2db91068b7 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -248,7 +248,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonPersonalDetails#9ec7863d = ReportReason; -userFull#29de80be flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings stars_rating:flags2.17?StarsRating = UserFull; +userFull#7e63ce1f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings stars_rating:flags2.17?StarsRating stars_my_pending_rating:flags2.18?StarsRating stars_my_pending_rating_date:flags2.18?int = UserFull; contact#145ade0b user_id:long mutual:Bool = Contact; @@ -2001,7 +2001,7 @@ storyAlbum#9325705a flags:# album_id:int title:string icon_photo:flags.0?Photo i stories.albumsNotModified#564edaeb = stories.Albums; stories.albums#c3987a3a hash:long albums:Vector = stories.Albums; -searchPostsFlood#940e707c flags:# remains:int wait_till:flags.0?int stars_amount:long = SearchPostsFlood; +searchPostsFlood#3e0b5b6a flags:# query_is_free:flags.0?true total_daily:int remains:int wait_till:flags.1?int stars_amount:long = SearchPostsFlood; ---functions--- @@ -2530,7 +2530,7 @@ channels.searchPosts#f2c4f24d flags:# hashtag:flags.0?string query:flags.1?strin channels.updatePaidMessagesPrice#4b12327b flags:# broadcast_messages_allowed:flags.0?true channel:InputChannel send_paid_messages_stars:long = Updates; channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates; channels.getMessageAuthor#ece2a0e6 channel:InputChannel id:int = User; -channels.checkSearchPostsFlood#bba9f121 = SearchPostsFlood; +channels.checkSearchPostsFlood#22567115 flags:# query:flags.0?string = SearchPostsFlood; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index c80ffec3e5..56468feb63 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media_common.h" #include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover #include "base/unixtime.h" +#include "boxes/sticker_set_box.h" #include "ui/effects/round_checkbox.h" #include "ui/effects/spoiler_mess.h" #include "ui/image/image.h" @@ -355,8 +356,11 @@ Photo::Photo( }) : nullptr) , _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0) -, _pinned(options.pinned) , _story(options.story) +, _storyPinned(options.storyPinned) +, _storyShowPinned(options.storyShowPinned) +, _storyHidden(options.storyHidden) +, _storyShowHidden(options.storyShowHidden) , _link(_sensitiveSpoiler ? HistoryView::MakeSensitiveMediaLink( std::make_shared(crl::guard(this, [=] { @@ -408,7 +412,7 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const || _dataMedia->image(Data::PhotoSize::Thumbnail)); if ((good && !_goodLoaded) || widthChanged) { _goodLoaded = good; - _pix = QPixmap(); + _pix = QImage(); if (_goodLoaded) { setPixFrom(_dataMedia->image(Data::PhotoSize::Large) ? _dataMedia->image(Data::PhotoSize::Large) @@ -426,7 +430,7 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const if (_pix.isNull()) { p.fillRect(0, 0, _width, _height, st::overviewPhotoBg); } else { - p.drawPixmap(0, 0, _pix); + p.drawImage(0, 0, _pix); } if (_spoiler) { @@ -442,11 +446,21 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const } } + if (_storyHidden) { + delegate()->hiddenMark()->paint( + p, + _pix, + _hiddenBgCache, + QPoint(), + QSize(_width, _height), + _width); + } + if (selected) { p.fillRect(0, 0, _width, _height, st::overviewPhotoSelectOverlay); } - if (_pinned) { + if (_storyPinned) { const auto &icon = selected ? st::storyPinnedIconSelected : st::storyPinnedIcon; @@ -466,8 +480,7 @@ void Photo::setPixFrom(not_null image) { if (!_goodLoaded) { img = Images::Blur(std::move(img)); } - _pix = Ui::PixmapFromImage( - CropMediaFrame(std::move(img), _width, _height)); + _pix = CropMediaFrame(std::move(img), _width, _height); // In case we have inline thumbnail we can unload all images and we still // won't get a blank image in the media viewer when the photo is opened. @@ -493,7 +506,7 @@ void Photo::clearSpoiler() { if (_spoiler) { _spoiler = nullptr; _sensitiveSpoiler = false; - _pix = QPixmap(); + _pix = QImage(); delegate()->repaintItem(this); } } @@ -506,9 +519,11 @@ void Photo::maybeClearSensitiveSpoiler() { } void Photo::itemDataChanged() { - const auto pinned = parent()->isPinned(); - if (_pinned != pinned) { - _pinned = pinned; + const auto pinned = _storyShowPinned && parent()->isPinned(); + const auto hidden = _storyShowHidden && !parent()->storyInProfile(); + if (_storyPinned != pinned || _storyHidden != hidden) { + _storyPinned = pinned; + _storyHidden = hidden; delegate()->repaintItem(this); } } @@ -541,8 +556,11 @@ Video::Video( }) : nullptr) , _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0) -, _pinned(options.pinned) -, _story(options.story) { +, _story(options.story) +, _storyPinned(options.storyPinned) +, _storyShowPinned(options.storyShowPinned) +, _storyHidden(options.storyHidden) +, _storyShowHidden(options.storyShowHidden) { setDocumentLinks(_data); if (_sensitiveSpoiler) { _openl = HistoryView::MakeSensitiveMediaLink( @@ -577,7 +595,11 @@ int32 Video::resizeGetHeight(int32 width) { return _height; } -void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { +void Video::paint( + Painter &p, + const QRect &clip, + TextSelection selection, + const PaintContext *context) { ensureDataMediaCreated(); const auto selected = (selection == FullSelection); @@ -614,15 +636,14 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const : thumbnail ? thumbnail->original() : Images::Blur(blurred->original()); - _pix = Ui::PixmapFromImage( - CropMediaFrame(std::move(img), _width, _height)); + _pix = CropMediaFrame(std::move(img), _width, _height); _pixBlurred = !(thumbnail || good); } if (_pix.isNull()) { p.fillRect(0, 0, _width, _height, st::overviewPhotoBg); } else { - p.drawPixmap(0, 0, _pix); + p.drawImage(0, 0, _pix); } if (_spoiler) { @@ -638,11 +659,21 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const } } + if (_storyHidden) { + delegate()->hiddenMark()->paint( + p, + _pix, + _hiddenBgCache, + QPoint(), + QSize(_width, _height), + _width); + } + if (selected) { p.fillRect(QRect(0, 0, _width, _height), st::overviewPhotoSelectOverlay); } - if (_pinned) { + if (_storyPinned) { const auto &icon = selected ? st::storyPinnedIconSelected : st::storyPinnedIcon; @@ -723,7 +754,7 @@ void Video::clearSpoiler() { if (_spoiler) { _spoiler = nullptr; _sensitiveSpoiler = false; - _pix = QPixmap(); + _pix = QImage(); delegate()->repaintItem(this); } } @@ -736,9 +767,11 @@ void Video::maybeClearSensitiveSpoiler() { } void Video::itemDataChanged() { - const auto pinned = parent()->isPinned(); - if (_pinned != pinned) { - _pinned = pinned; + const auto pinned = _storyShowPinned && parent()->isPinned(); + const auto hidden = _storyShowHidden && !parent()->storyInProfile(); + if (_storyPinned != pinned || _storyHidden != hidden) { + _storyPinned = pinned; + _storyHidden = hidden; delegate()->repaintItem(this); } } diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 5edf2a6ac4..74402d4985 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -190,8 +190,11 @@ struct Info : RuntimeComponent { struct MediaOptions { bool spoiler = false; - bool pinned = false; bool story = false; + bool storyPinned = false; + bool storyShowPinned = false; + bool storyHidden = false; + bool storyShowHidden = false; }; class Photo final : public ItemBase { @@ -225,11 +228,15 @@ private: mutable std::shared_ptr _dataMedia; std::unique_ptr _spoiler; - QPixmap _pix; - bool _goodLoaded = false; - bool _sensitiveSpoiler = false; - bool _pinned = false; - bool _story = false; + QImage _pix; + QImage _hiddenBgCache; + bool _goodLoaded : 1 = false; + bool _sensitiveSpoiler : 1 = false; + bool _story : 1 = false; + bool _storyPinned : 1 = false; + bool _storyShowPinned : 1 = false; + bool _storyHidden : 1 = false; + bool _storyShowHidden : 1 = false; ClickHandlerPtr _link; @@ -339,11 +346,15 @@ private: QString _duration; std::unique_ptr _spoiler; - QPixmap _pix; - bool _pixBlurred = true; - bool _sensitiveSpoiler = false; - bool _pinned = false; - bool _story = false; + QImage _pix; + QImage _hiddenBgCache; + bool _pixBlurred : 1 = true; + bool _sensitiveSpoiler : 1 = false; + bool _story : 1 = false; + bool _storyPinned : 1 = false; + bool _storyShowPinned : 1 = false; + bool _storyHidden : 1 = false; + bool _storyShowHidden : 1 = false; }; diff --git a/Telegram/SourceFiles/overview/overview_layout_delegate.h b/Telegram/SourceFiles/overview/overview_layout_delegate.h index e2fc7717d6..0b81b33583 100644 --- a/Telegram/SourceFiles/overview/overview_layout_delegate.h +++ b/Telegram/SourceFiles/overview/overview_layout_delegate.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +class StickerPremiumMark; + namespace Overview { namespace Layout { @@ -19,6 +21,8 @@ public: virtual void repaintItem(not_null item) = 0; virtual bool itemVisible(not_null item) = 0; + [[nodiscard]] virtual not_null hiddenMark() = 0; + virtual void openPhoto(not_null photo, FullMsgId id) = 0; virtual void openDocument( not_null document,