2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-22 02:07:24 +00:00

Display hidden state in story albums.

This commit is contained in:
John Preston 2025-07-29 12:27:29 +04:00
parent 677891d0ff
commit 953487bb65
21 changed files with 247 additions and 103 deletions

11
.gitignore vendored
View File

@ -53,3 +53,14 @@ stage
*.*~ *.*~
.idea/ .idea/
cmake-build-debug/ 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/

View File

@ -1945,6 +1945,35 @@ void Stories::albumRename(
}).send(); }).send();
} }
void Stories::albumDelete(not_null<PeerData*> 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) { void Stories::notifyAlbumUpdate(StoryAlbumUpdate &&update) {
const auto peerId = update.peer->id; const auto peerId = update.peer->id;
const auto i = _albums.find(peerId); const auto i = _albums.find(peerId);

View File

@ -235,6 +235,7 @@ public:
const QString &title, const QString &title,
Fn<void(StoryAlbum)> done, Fn<void(StoryAlbum)> done,
Fn<void(QString)> fail); Fn<void(QString)> fail);
void albumDelete(not_null<PeerData*> peer, int id);
void notifyAlbumUpdate(StoryAlbumUpdate &&update); void notifyAlbumUpdate(StoryAlbumUpdate &&update);
[[nodiscard]] rpl::producer<StoryAlbumUpdate> albumUpdates() const; [[nodiscard]] rpl::producer<StoryAlbumUpdate> albumUpdates() const;

View File

@ -367,7 +367,12 @@ bool Story::pinnedToTop() const {
} }
void Story::setInProfile(bool value) { 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 { bool Story::inProfile() const {

View File

@ -356,6 +356,8 @@ enum class MessageFlag : uint64 {
StarsPaidSuggested = (1ULL << 52), StarsPaidSuggested = (1ULL << 52),
TonPaidSuggested = (1ULL << 53), TonPaidSuggested = (1ULL << 53),
StoryInProfile = (1ULL << 54),
}; };
inline constexpr bool is_flag_type(MessageFlag) { return true; } inline constexpr bool is_flag_type(MessageFlag) { return true; }
using MessageFlags = base::flags<MessageFlag>; using MessageFlags = base::flags<MessageFlag>;

View File

@ -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() { void HistoryItem::returnSavedMedia() {
if (!isEditingMedia()) { if (!isEditingMedia()) {
return; return;
@ -2014,6 +2025,11 @@ void HistoryItem::setStoryFields(not_null<Data::Story*> story) {
} else { } else {
_flags &= ~MessageFlag::Pinned; _flags &= ~MessageFlag::Pinned;
} }
if (story->inProfile()) {
_flags |= MessageFlag::StoryInProfile;
} else {
_flags &= ~MessageFlag::StoryInProfile;
}
} }
void HistoryItem::applyEdition(const MTPDmessageService &message) { void HistoryItem::applyEdition(const MTPDmessageService &message) {

View File

@ -235,6 +235,9 @@ public:
[[nodiscard]] bool invertMedia() const { [[nodiscard]] bool invertMedia() const {
return _flags & MessageFlag::InvertMedia; return _flags & MessageFlag::InvertMedia;
} }
[[nodiscard]] bool storyInProfile() const {
return _flags & MessageFlag::StoryInProfile;
}
[[nodiscard]] bool unread(not_null<Data::Thread*> thread) const; [[nodiscard]] bool unread(not_null<Data::Thread*> thread) const;
[[nodiscard]] bool showNotification() const; [[nodiscard]] bool showNotification() const;
void markClientSideAsRead(); void markClientSideAsRead();
@ -250,6 +253,7 @@ public:
void markMediaAndMentionRead(); void markMediaAndMentionRead();
bool markContentsRead(bool fromThisClient = false); bool markContentsRead(bool fromThisClient = false);
void setIsPinned(bool isPinned); void setIsPinned(bool isPinned);
void setStoryInProfile(bool inProfile);
// For edit media in history_message. // For edit media in history_message.
void returnSavedMedia(); void returnSavedMedia();

View File

@ -588,10 +588,6 @@ void TopBar::setStories(rpl::producer<Dialogs::Stories::Content> content) {
updateControlsVisibility(anim::type::instant); updateControlsVisibility(anim::type::instant);
} }
void TopBar::setStoriesArchive(bool archive) {
_storiesArchive = archive;
}
void TopBar::setSelectedItems(SelectedItems &&items) { void TopBar::setSelectedItems(SelectedItems &&items) {
auto wasSelectionMode = selectionMode(); auto wasSelectionMode = selectionMode();
_selectedItems = std::move(items); _selectedItems = std::move(items);
@ -628,10 +624,19 @@ void TopBar::updateSelectionState() {
_canDelete = computeCanDelete(); _canDelete = computeCanDelete();
_canForward = computeCanForward(); _canForward = computeCanForward();
_canUnpinStories = computeCanUnpinStories(); _canUnpinStories = computeCanUnpinStories();
_canToggleStoryPin = computeCanToggleStoryPin();
_allStoriesInProfile = computeAllStoriesInProfile();
_selectionText->entity()->setValue(generateSelectedText()); _selectionText->entity()->setValue(generateSelectedText());
_delete->toggle(_canDelete, anim::type::instant); _delete->toggle(_canDelete, anim::type::instant);
_forward->toggle(_canForward, anim::type::instant); _forward->toggle(_canForward, anim::type::instant);
_toggleStoryInProfile->toggle(_canToggleStoryPin, 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->toggle(_canToggleStoryPin, anim::type::instant);
_toggleStoryPin->entity()->setIconOverride( _toggleStoryPin->entity()->setIconOverride(
_canUnpinStories ? &_st.storiesUnpin.icon : nullptr, _canUnpinStories ? &_st.storiesUnpin.icon : nullptr,
@ -652,6 +657,7 @@ void TopBar::createSelectionControls() {
_canForward = computeCanForward(); _canForward = computeCanForward();
_canUnpinStories = computeCanUnpinStories(); _canUnpinStories = computeCanUnpinStories();
_canToggleStoryPin = computeCanToggleStoryPin(); _canToggleStoryPin = computeCanToggleStoryPin();
_allStoriesInProfile = computeAllStoriesInProfile();
_cancelSelection = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>( _cancelSelection = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
this, this,
object_ptr<Ui::IconButton>(this, _st.mediaCancel), object_ptr<Ui::IconButton>(this, _st.mediaCancel),
@ -710,16 +716,18 @@ void TopBar::createSelectionControls() {
this, this,
object_ptr<Ui::IconButton>( object_ptr<Ui::IconButton>(
this, this,
_storiesArchive ? _st.storiesSave : _st.storiesArchive), _allStoriesInProfile ? _st.storiesArchive : _st.storiesSave),
st::infoTopBarScale)); st::infoTopBarScale));
registerToggleControlCallback( registerToggleControlCallback(
_toggleStoryInProfile.data(), _toggleStoryInProfile.data(),
[this] { return selectionMode() && _canToggleStoryPin; }); [this] { return selectionMode() && _canToggleStoryPin; });
_toggleStoryInProfile->setDuration(st::infoTopBarDuration); _toggleStoryInProfile->setDuration(st::infoTopBarDuration);
_toggleStoryInProfile->entity()->clicks( _toggleStoryInProfile->entity()->clicks(
) | rpl::map_to( ) | rpl::map([=] {
SelectionAction::ToggleStoryInProfile return _allStoriesInProfile
) | rpl::start_to_stream( ? SelectionAction::ToggleStoryToArchive
: SelectionAction::ToggleStoryToProfile;
}) | rpl::start_to_stream(
_selectionActionRequests, _selectionActionRequests,
_cancelSelection->lifetime()); _cancelSelection->lifetime());
_toggleStoryInProfile->entity()->setVisible(_canToggleStoryPin); _toggleStoryInProfile->entity()->setVisible(_canToggleStoryPin);
@ -769,6 +777,12 @@ bool TopBar::computeCanToggleStoryPin() const {
&SelectedItem::canToggleStoryPin); &SelectedItem::canToggleStoryPin);
} }
bool TopBar::computeAllStoriesInProfile() const {
return ranges::all_of(
_selectedItems.list,
&SelectedItem::storyInProfile);
}
Ui::StringWithNumbers TopBar::generateSelectedText() const { Ui::StringWithNumbers TopBar::generateSelectedText() const {
return _selectedItems.title(_selectedItems.list.size()); return _selectedItems.title(_selectedItems.list.size());
} }

View File

@ -63,7 +63,6 @@ public:
void setTitle(TitleDescriptor descriptor); void setTitle(TitleDescriptor descriptor);
void setStories(rpl::producer<Dialogs::Stories::Content> content); void setStories(rpl::producer<Dialogs::Stories::Content> content);
void setStoriesArchive(bool archive);
void enableBackButton(); void enableBackButton();
void highlight(); void highlight();
@ -131,6 +130,7 @@ private:
[[nodiscard]] bool computeCanForward() const; [[nodiscard]] bool computeCanForward() const;
[[nodiscard]] bool computeCanUnpinStories() const; [[nodiscard]] bool computeCanUnpinStories() const;
[[nodiscard]] bool computeCanToggleStoryPin() const; [[nodiscard]] bool computeCanToggleStoryPin() const;
[[nodiscard]] bool computeAllStoriesInProfile() const;
void updateSelectionState(); void updateSelectionState();
void createSelectionControls(); void createSelectionControls();
@ -178,7 +178,7 @@ private:
bool _canForward = false; bool _canForward = false;
bool _canToggleStoryPin = false; bool _canToggleStoryPin = false;
bool _canUnpinStories = false; bool _canUnpinStories = false;
bool _storiesArchive = false; bool _allStoriesInProfile = false;
QPointer<Ui::FadeWrap<Ui::IconButton>> _cancelSelection; QPointer<Ui::FadeWrap<Ui::IconButton>> _cancelSelection;
QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText; QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText;
QPointer<Ui::FadeWrap<Ui::IconButton>> _forward; QPointer<Ui::FadeWrap<Ui::IconButton>> _forward;

View File

@ -657,8 +657,6 @@ void WrapWidget::finishShowContent() {
.subtitle = _content->subtitle(), .subtitle = _content->subtitle(),
}); });
_topBar->setStories(_content->titleStories()); _topBar->setStories(_content->titleStories());
_topBar->setStoriesArchive(
_controller->key().storiesAlbumId() == Stories::ArchiveId());
} }
_desiredHeights.fire(desiredHeightForContent()); _desiredHeights.fire(desiredHeightForContent());
_desiredShadowVisibilities.fire(_content->desiredShadowVisibility()); _desiredShadowVisibilities.fire(_content->desiredShadowVisibility());
@ -787,7 +785,6 @@ bool WrapWidget::showInternal(
&& (params.way == Window::SectionShow::Way::ClearStack); && (params.way == Window::SectionShow::Way::ClearStack);
if (_controller->validateMementoPeer(content)) { if (_controller->validateMementoPeer(content)) {
if (!skipInternal && _content->showInternal(content)) { if (!skipInternal && _content->showInternal(content)) {
highlightTopBar();
return true; return true;
} }
} }

View File

@ -65,6 +65,7 @@ struct SelectedItem {
bool canForward = false; bool canForward = false;
bool canToggleStoryPin = false; bool canToggleStoryPin = false;
bool canUnpinStory = false; bool canUnpinStory = false;
bool storyInProfile = false;
}; };
struct SelectedItems { struct SelectedItems {
@ -80,7 +81,8 @@ enum class SelectionAction {
Forward, Forward,
Delete, Delete,
ToggleStoryPin, ToggleStoryPin,
ToggleStoryInProfile, ToggleStoryToProfile,
ToggleStoryToArchive,
}; };
class WrapWidget final : public Window::SectionWidget { class WrapWidget final : public Window::SectionWidget {

View File

@ -32,6 +32,7 @@ struct ListItemSelectionData {
bool canForward = false; bool canForward = false;
bool canToggleStoryPin = false; bool canToggleStoryPin = false;
bool canUnpinStory = false; bool canUnpinStory = false;
bool storyInProfile = false;
friend inline bool operator==( friend inline bool operator==(
ListItemSelectionData, ListItemSelectionData,

View File

@ -58,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "boxes/delete_messages_box.h" #include "boxes/delete_messages_box.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/sticker_set_box.h" // StickerPremiumMark
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "core/application.h" #include "core/application.h"
#include "ui/toast/toast.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_layers.h"
#include "styles/style_menu_icons.h" #include "styles/style_menu_icons.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_credits.h" // giftBoxHiddenMark
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
@ -152,7 +154,11 @@ ListWidget::ListWidget(
_provider->type(), _provider->type(),
[=] { scrollDateCheck(); }, [=] { scrollDateCheck(); },
[=] { scrollDateHide(); })) [=] { scrollDateHide(); }))
, _storiesAddToAlbumId(controller->storiesAddToAlbumId()) { , _storiesAddToAlbumId(controller->storiesAddToAlbumId())
, _hiddenMark(std::make_unique<StickerPremiumMark>(
&_controller->session(),
st::giftBoxHiddenMark,
RectPart::Center)) {
start(); start();
} }
@ -312,8 +318,11 @@ void ListWidget::selectionAction(SelectionAction action) {
case SelectionAction::Clear: clearSelected(); return; case SelectionAction::Clear: clearSelected(); return;
case SelectionAction::Forward: forwardSelected(); return; case SelectionAction::Forward: forwardSelected(); return;
case SelectionAction::Delete: deleteSelected(); return; case SelectionAction::Delete: deleteSelected(); return;
case SelectionAction::ToggleStoryInProfile: case SelectionAction::ToggleStoryToProfile:
toggleStoryInProfileSelected(); toggleStoryInProfileSelected(true);
return;
case SelectionAction::ToggleStoryToArchive:
toggleStoryInProfileSelected(false);
return; return;
case SelectionAction::ToggleStoryPin: toggleStoryPinSelected(); return; case SelectionAction::ToggleStoryPin: toggleStoryPinSelected(); return;
} }
@ -395,6 +404,7 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems {
result.canForward = selection.canForward; result.canForward = selection.canForward;
result.canToggleStoryPin = selection.canToggleStoryPin; result.canToggleStoryPin = selection.canToggleStoryPin;
result.canUnpinStory = selection.canUnpinStory; result.canUnpinStory = selection.canUnpinStory;
result.storyInProfile = selection.storyInProfile;
return result; return result;
}; };
auto transformation = [&](const auto &item) { auto transformation = [&](const auto &item) {
@ -527,6 +537,10 @@ bool ListWidget::itemVisible(not_null<const BaseLayout*> item) {
return true; return true;
} }
not_null<StickerPremiumMark*> ListWidget::hiddenMark() {
return _hiddenMark.get();
}
QString ListWidget::tooltipText() const { QString ListWidget::tooltipText() const {
if (const auto link = ClickHandler::getActive()) { if (const auto link = ClickHandler::getActive()) {
return link->tooltip(); return link->tooltip();
@ -1020,6 +1034,11 @@ void ListWidget::showContextMenu(
return !item.second.canToggleStoryPin; return !item.second.canToggleStoryPin;
}); });
}; };
const auto allInProfile = [&] {
return ranges::all_of(_selected, [](auto &&item) {
return item.second.storyInProfile;
});
};
const auto canUnpinStoryAll = [&] { const auto canUnpinStoryAll = [&] {
return ranges::any_of(_selected, [](auto &&item) { return ranges::any_of(_selected, [](auto &&item) {
return item.second.canUnpinStory; return item.second.canUnpinStory;
@ -1125,13 +1144,14 @@ void ListWidget::showContextMenu(
} }
if (overSelected == SelectionState::OverSelectedItems) { if (overSelected == SelectionState::OverSelectedItems) {
if (canToggleStoryPinAll()) { if (canToggleStoryPinAll()) {
const auto albumId = _controller->storiesAlbumId(); const auto toProfile = !allInProfile();
const auto toProfile = (albumId == Stories::ArchiveId());
_contextMenu->addAction( _contextMenu->addAction(
(toProfile (toProfile
? tr::lng_mediaview_save_to_profile ? tr::lng_mediaview_save_to_profile
: tr::lng_archived_add)(tr::now), : tr::lng_archived_add)(tr::now),
crl::guard(this, [this] { toggleStoryInProfileSelected(); }), crl::guard(this, [=] {
toggleStoryInProfileSelected(toProfile);
}),
(toProfile (toProfile
? &st::menuIconStoriesSave ? &st::menuIconStoriesSave
: &st::menuIconStoriesArchive)); : &st::menuIconStoriesArchive));
@ -1177,14 +1197,15 @@ void ListWidget::showContextMenu(
item, item,
FullSelection); FullSelection);
if (selectionData.canToggleStoryPin) { if (selectionData.canToggleStoryPin) {
const auto albumId = _controller->storiesAlbumId(); const auto toProfile = !selectionData.storyInProfile;
const auto toProfile = (albumId == Stories::ArchiveId());
_contextMenu->addAction( _contextMenu->addAction(
(toProfile (toProfile
? tr::lng_mediaview_save_to_profile ? tr::lng_mediaview_save_to_profile
: tr::lng_mediaview_archive_story)(tr::now), : tr::lng_mediaview_archive_story)(tr::now),
crl::guard(this, [=] { crl::guard(this, [=] {
toggleStoryInProfile({ 1, globalId.itemId }); toggleStoryInProfile(
{ 1, globalId.itemId },
toProfile);
}), }),
(toProfile (toProfile
? &st::menuIconStoriesSave ? &st::menuIconStoriesSave
@ -1317,10 +1338,11 @@ void ListWidget::deleteSelected() {
})); }));
} }
void ListWidget::toggleStoryInProfileSelected() { void ListWidget::toggleStoryInProfileSelected(bool toProfile) {
toggleStoryInProfile(collectSelectedIds(), crl::guard(this, [=] { toggleStoryInProfile(
clearSelected(); collectSelectedIds(),
})); toProfile,
crl::guard(this, [=] { clearSelected(); }));
} }
void ListWidget::toggleStoryPinSelected() { void ListWidget::toggleStoryPinSelected() {
@ -1335,6 +1357,7 @@ void ListWidget::toggleStoryPinSelected() {
void ListWidget::toggleStoryInProfile( void ListWidget::toggleStoryInProfile(
MessageIdsList &&items, MessageIdsList &&items,
bool toProfile,
Fn<void()> confirmed) { Fn<void()> confirmed) {
auto list = std::vector<FullStoryId>(); auto list = std::vector<FullStoryId>();
for (const auto &id : items) { for (const auto &id : items) {
@ -1347,8 +1370,6 @@ void ListWidget::toggleStoryInProfile(
} }
const auto channel = peerIsChannel(list.front().peer); const auto channel = peerIsChannel(list.front().peer);
const auto count = int(list.size()); const auto count = int(list.size());
const auto albumId = _controller->storiesAlbumId();
const auto toProfile = (albumId == Stories::ArchiveId());
const auto controller = _controller; const auto controller = _controller;
const auto sure = [=](Fn<void()> close) { const auto sure = [=](Fn<void()> close) {
using namespace ::Media::Stories; using namespace ::Media::Stories;

View File

@ -84,6 +84,7 @@ public:
void unregisterHeavyItem(not_null<const BaseLayout*> item) override; void unregisterHeavyItem(not_null<const BaseLayout*> item) override;
void repaintItem(not_null<const BaseLayout*> item) override; void repaintItem(not_null<const BaseLayout*> item) override;
bool itemVisible(not_null<const BaseLayout*> item) override; bool itemVisible(not_null<const BaseLayout*> item) override;
not_null<StickerPremiumMark*> hiddenMark() override;
// AbstractTooltipShower interface // AbstractTooltipShower interface
QString tooltipText() const override; QString tooltipText() const override;
@ -192,11 +193,12 @@ private:
void forwardItems(MessageIdsList &&items); void forwardItems(MessageIdsList &&items);
void deleteSelected(); void deleteSelected();
void toggleStoryPinSelected(); void toggleStoryPinSelected();
void toggleStoryInProfileSelected(); void toggleStoryInProfileSelected(bool toProfile);
void deleteItem(GlobalMsgId globalId); void deleteItem(GlobalMsgId globalId);
void deleteItems(SelectedItems &&items, Fn<void()> confirmed = nullptr); void deleteItems(SelectedItems &&items, Fn<void()> confirmed = nullptr);
void toggleStoryInProfile( void toggleStoryInProfile(
MessageIdsList &&items, MessageIdsList &&items,
bool toProfile,
Fn<void()> confirmed = nullptr); Fn<void()> confirmed = nullptr);
void toggleStoryPin( void toggleStoryPin(
MessageIdsList &&items, MessageIdsList &&items,
@ -311,6 +313,7 @@ private:
int _storiesAddToAlbumId = 0; int _storiesAddToAlbumId = 0;
base::flat_set<StoryId> _storiesInAlbum; base::flat_set<StoryId> _storiesInAlbum;
base::flat_set<MsgId> _storyMsgsToMarkSelected; base::flat_set<MsgId> _storyMsgsToMarkSelected;
std::unique_ptr<StickerPremiumMark> _hiddenMark;
base::unique_qptr<Ui::PopupMenu> _contextMenu; base::unique_qptr<Ui::PopupMenu> _contextMenu;
rpl::event_stream<> _checkForHide; rpl::event_stream<> _checkForHide;

View File

@ -947,10 +947,8 @@ void InnerWidget::refreshAbout() {
auto text = tr::lng_peer_gifts_empty_search( auto text = tr::lng_peer_gifts_empty_search(
tr::now, tr::now,
Ui::Text::RichLangValue); Ui::Text::RichLangValue);
if (_entries->total > 0) { text.append("\n\n").append(Ui::Text::Link(
text.append("\n\n").append(Ui::Text::Link( tr::lng_peer_gifts_view_all(tr::now)));
tr::lng_peer_gifts_view_all(tr::now)));
}
auto about = std::make_unique<Ui::FlatLabel>( auto about = std::make_unique<Ui::FlatLabel>(
this, this,
rpl::single(text), rpl::single(text),

View File

@ -552,17 +552,18 @@ void InnerWidget::setupEmpty() {
), ),
_list->heightValue() _list->heightValue()
) | rpl::start_with_next([=](auto, int listHeight) { ) | rpl::start_with_next([=](auto, int listHeight) {
if (listHeight) { const auto padding = st::infoMediaMargin;
_empty.destroy(); if (const auto raw = _empty.release()) {
return; raw->hide();
raw->deleteLater();
}
if (listHeight <= padding.bottom() + padding.top()) {
refreshEmpty();
} }
refreshEmpty();
}, _list->lifetime()); }, _list->lifetime());
} }
void InnerWidget::refreshEmpty() { void InnerWidget::refreshEmpty() {
_empty.destroy();
const auto albumId = _albumId.current(); const auto albumId = _albumId.current();
const auto stories = &_controller->session().data().stories(); const auto stories = &_controller->session().data().stories();
const auto knownEmpty = stories->albumIdsCountKnown(_peer->id, albumId); const auto knownEmpty = stories->albumIdsCountKnown(_peer->id, albumId);
@ -606,7 +607,6 @@ void InnerWidget::refreshEmpty() {
button->setClickedCallback([=] { button->setClickedCallback([=] {
editAlbumStories(albumId); editAlbumStories(albumId);
}); });
empty->show(); empty->show();
_empty = std::move(empty); _empty = std::move(empty);
} else { } else {
@ -790,10 +790,11 @@ void InnerWidget::editAlbumName(int id) {
void InnerWidget::confirmDeleteAlbum(int id) { void InnerWidget::confirmDeleteAlbum(int id) {
const auto done = [=](Fn<void()> close) { const auto done = [=](Fn<void()> close) {
_controller->session().api().request(
MTPstories_DeleteAlbum(_peer->input, MTP_int(id))
).send();
albumRemoved(id); albumRemoved(id);
const auto stories = &_controller->session().data().stories();
stories->albumDelete(_peer, id);
close(); close();
}; };
_controller->uiShow()->show(Ui::MakeConfirmBox({ _controller->uiShow()->show(Ui::MakeConfirmBox({
@ -807,7 +808,7 @@ void InnerWidget::confirmDeleteAlbum(int id) {
void InnerWidget::albumAdded(Data::StoryAlbum result) { void InnerWidget::albumAdded(Data::StoryAlbum result) {
Expects(ranges::contains(_albums, result.id, &Data::StoryAlbum::id)); Expects(ranges::contains(_albums, result.id, &Data::StoryAlbum::id));
_albumId = result.id; _albumIdChanges.fire_copy(result.id);
} }
void InnerWidget::albumRenamed(int id, QString name) { void InnerWidget::albumRenamed(int id, QString name) {
@ -821,20 +822,8 @@ void InnerWidget::albumRenamed(int id, QString name) {
void InnerWidget::albumRemoved(int id) { void InnerWidget::albumRemoved(int id) {
auto now = _albumId.current(); auto now = _albumId.current();
if (now == id) { 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); const auto i = ranges::find(_albums, id, &Data::StoryAlbum::id);
if (i != end(_albums)) { if (i != end(_albums)) {
_albums.erase(i); _albums.erase(i);
@ -908,14 +897,6 @@ int InnerWidget::recountHeight() {
} }
void InnerWidget::setScrollHeightValue(rpl::producer<int> value) { void InnerWidget::setScrollHeightValue(rpl::producer<int> 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<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const { rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {

View File

@ -343,10 +343,20 @@ std::unique_ptr<BaseLayout> Provider::createLayout(
} }
return nullptr; 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; using namespace Overview::Layout;
const auto options = MediaOptions{ const auto options = MediaOptions{
.pinned = item->isPinned(),
.story = true, .story = true,
.storyPinned = showPinned && item->isPinned(),
.storyShowPinned = showPinned,
.storyHidden = showHidden && !item->storyInProfile(),
.storyShowHidden = showHidden,
}; };
if (const auto photo = getPhoto()) { if (const auto photo = getPhoto()) {
return std::make_unique<Photo>(delegate, item, photo, options); return std::make_unique<Photo>(delegate, item, photo, options);
@ -379,6 +389,7 @@ ListItemSelectionData Provider::computeSelectionData(
result.canForward = peer->isSelf() && story->canShare(); result.canForward = peer->isSelf() && story->canShare();
result.canDelete = story->canDelete(); result.canDelete = story->canDelete();
result.canUnpinStory = story->pinnedToTop(); result.canUnpinStory = story->pinnedToTop();
result.storyInProfile = story->inProfile();
} }
result.canToggleStoryPin = peer->isSelf() result.canToggleStoryPin = peer->isSelf()
|| (channel && channel->canEditStories()); || (channel && channel->canEditStories());

View File

@ -248,7 +248,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = 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; 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.albumsNotModified#564edaeb = stories.Albums;
stories.albums#c3987a3a hash:long albums:Vector<StoryAlbum> = stories.Albums; stories.albums#c3987a3a hash:long albums:Vector<StoryAlbum> = 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--- ---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.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.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
channels.getMessageAuthor#ece2a0e6 channel:InputChannel id:int = User; 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.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;

View File

@ -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_media_common.h"
#include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover #include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover
#include "base/unixtime.h" #include "base/unixtime.h"
#include "boxes/sticker_set_box.h"
#include "ui/effects/round_checkbox.h" #include "ui/effects/round_checkbox.h"
#include "ui/effects/spoiler_mess.h" #include "ui/effects/spoiler_mess.h"
#include "ui/image/image.h" #include "ui/image/image.h"
@ -355,8 +356,11 @@ Photo::Photo(
}) })
: nullptr) : nullptr)
, _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0) , _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)
, _link(_sensitiveSpoiler , _link(_sensitiveSpoiler
? HistoryView::MakeSensitiveMediaLink( ? HistoryView::MakeSensitiveMediaLink(
std::make_shared<LambdaClickHandler>(crl::guard(this, [=] { std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
@ -408,7 +412,7 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const
|| _dataMedia->image(Data::PhotoSize::Thumbnail)); || _dataMedia->image(Data::PhotoSize::Thumbnail));
if ((good && !_goodLoaded) || widthChanged) { if ((good && !_goodLoaded) || widthChanged) {
_goodLoaded = good; _goodLoaded = good;
_pix = QPixmap(); _pix = QImage();
if (_goodLoaded) { if (_goodLoaded) {
setPixFrom(_dataMedia->image(Data::PhotoSize::Large) setPixFrom(_dataMedia->image(Data::PhotoSize::Large)
? _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()) { if (_pix.isNull()) {
p.fillRect(0, 0, _width, _height, st::overviewPhotoBg); p.fillRect(0, 0, _width, _height, st::overviewPhotoBg);
} else { } else {
p.drawPixmap(0, 0, _pix); p.drawImage(0, 0, _pix);
} }
if (_spoiler) { 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) { if (selected) {
p.fillRect(0, 0, _width, _height, st::overviewPhotoSelectOverlay); p.fillRect(0, 0, _width, _height, st::overviewPhotoSelectOverlay);
} }
if (_pinned) { if (_storyPinned) {
const auto &icon = selected const auto &icon = selected
? st::storyPinnedIconSelected ? st::storyPinnedIconSelected
: st::storyPinnedIcon; : st::storyPinnedIcon;
@ -466,8 +480,7 @@ void Photo::setPixFrom(not_null<Image*> image) {
if (!_goodLoaded) { if (!_goodLoaded) {
img = Images::Blur(std::move(img)); img = Images::Blur(std::move(img));
} }
_pix = Ui::PixmapFromImage( _pix = CropMediaFrame(std::move(img), _width, _height);
CropMediaFrame(std::move(img), _width, _height));
// In case we have inline thumbnail we can unload all images and we still // 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. // won't get a blank image in the media viewer when the photo is opened.
@ -493,7 +506,7 @@ void Photo::clearSpoiler() {
if (_spoiler) { if (_spoiler) {
_spoiler = nullptr; _spoiler = nullptr;
_sensitiveSpoiler = false; _sensitiveSpoiler = false;
_pix = QPixmap(); _pix = QImage();
delegate()->repaintItem(this); delegate()->repaintItem(this);
} }
} }
@ -506,9 +519,11 @@ void Photo::maybeClearSensitiveSpoiler() {
} }
void Photo::itemDataChanged() { void Photo::itemDataChanged() {
const auto pinned = parent()->isPinned(); const auto pinned = _storyShowPinned && parent()->isPinned();
if (_pinned != pinned) { const auto hidden = _storyShowHidden && !parent()->storyInProfile();
_pinned = pinned; if (_storyPinned != pinned || _storyHidden != hidden) {
_storyPinned = pinned;
_storyHidden = hidden;
delegate()->repaintItem(this); delegate()->repaintItem(this);
} }
} }
@ -541,8 +556,11 @@ Video::Video(
}) })
: nullptr) : nullptr)
, _sensitiveSpoiler(parent->isMediaSensitive() ? 1 : 0) , _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); setDocumentLinks(_data);
if (_sensitiveSpoiler) { if (_sensitiveSpoiler) {
_openl = HistoryView::MakeSensitiveMediaLink( _openl = HistoryView::MakeSensitiveMediaLink(
@ -577,7 +595,11 @@ int32 Video::resizeGetHeight(int32 width) {
return _height; 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(); ensureDataMediaCreated();
const auto selected = (selection == FullSelection); const auto selected = (selection == FullSelection);
@ -614,15 +636,14 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
: thumbnail : thumbnail
? thumbnail->original() ? thumbnail->original()
: Images::Blur(blurred->original()); : Images::Blur(blurred->original());
_pix = Ui::PixmapFromImage( _pix = CropMediaFrame(std::move(img), _width, _height);
CropMediaFrame(std::move(img), _width, _height));
_pixBlurred = !(thumbnail || good); _pixBlurred = !(thumbnail || good);
} }
if (_pix.isNull()) { if (_pix.isNull()) {
p.fillRect(0, 0, _width, _height, st::overviewPhotoBg); p.fillRect(0, 0, _width, _height, st::overviewPhotoBg);
} else { } else {
p.drawPixmap(0, 0, _pix); p.drawImage(0, 0, _pix);
} }
if (_spoiler) { 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) { if (selected) {
p.fillRect(QRect(0, 0, _width, _height), st::overviewPhotoSelectOverlay); p.fillRect(QRect(0, 0, _width, _height), st::overviewPhotoSelectOverlay);
} }
if (_pinned) { if (_storyPinned) {
const auto &icon = selected const auto &icon = selected
? st::storyPinnedIconSelected ? st::storyPinnedIconSelected
: st::storyPinnedIcon; : st::storyPinnedIcon;
@ -723,7 +754,7 @@ void Video::clearSpoiler() {
if (_spoiler) { if (_spoiler) {
_spoiler = nullptr; _spoiler = nullptr;
_sensitiveSpoiler = false; _sensitiveSpoiler = false;
_pix = QPixmap(); _pix = QImage();
delegate()->repaintItem(this); delegate()->repaintItem(this);
} }
} }
@ -736,9 +767,11 @@ void Video::maybeClearSensitiveSpoiler() {
} }
void Video::itemDataChanged() { void Video::itemDataChanged() {
const auto pinned = parent()->isPinned(); const auto pinned = _storyShowPinned && parent()->isPinned();
if (_pinned != pinned) { const auto hidden = _storyShowHidden && !parent()->storyInProfile();
_pinned = pinned; if (_storyPinned != pinned || _storyHidden != hidden) {
_storyPinned = pinned;
_storyHidden = hidden;
delegate()->repaintItem(this); delegate()->repaintItem(this);
} }
} }

View File

@ -190,8 +190,11 @@ struct Info : RuntimeComponent<Info, LayoutItemBase> {
struct MediaOptions { struct MediaOptions {
bool spoiler = false; bool spoiler = false;
bool pinned = false;
bool story = false; bool story = false;
bool storyPinned = false;
bool storyShowPinned = false;
bool storyHidden = false;
bool storyShowHidden = false;
}; };
class Photo final : public ItemBase { class Photo final : public ItemBase {
@ -225,11 +228,15 @@ private:
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia; mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
std::unique_ptr<Ui::SpoilerAnimation> _spoiler; std::unique_ptr<Ui::SpoilerAnimation> _spoiler;
QPixmap _pix; QImage _pix;
bool _goodLoaded = false; QImage _hiddenBgCache;
bool _sensitiveSpoiler = false; bool _goodLoaded : 1 = false;
bool _pinned = false; bool _sensitiveSpoiler : 1 = false;
bool _story = false; bool _story : 1 = false;
bool _storyPinned : 1 = false;
bool _storyShowPinned : 1 = false;
bool _storyHidden : 1 = false;
bool _storyShowHidden : 1 = false;
ClickHandlerPtr _link; ClickHandlerPtr _link;
@ -339,11 +346,15 @@ private:
QString _duration; QString _duration;
std::unique_ptr<Ui::SpoilerAnimation> _spoiler; std::unique_ptr<Ui::SpoilerAnimation> _spoiler;
QPixmap _pix; QImage _pix;
bool _pixBlurred = true; QImage _hiddenBgCache;
bool _sensitiveSpoiler = false; bool _pixBlurred : 1 = true;
bool _pinned = false; bool _sensitiveSpoiler : 1 = false;
bool _story = false; bool _story : 1 = false;
bool _storyPinned : 1 = false;
bool _storyShowPinned : 1 = false;
bool _storyHidden : 1 = false;
bool _storyShowHidden : 1 = false;
}; };

View File

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
class StickerPremiumMark;
namespace Overview { namespace Overview {
namespace Layout { namespace Layout {
@ -19,6 +21,8 @@ public:
virtual void repaintItem(not_null<const ItemBase*> item) = 0; virtual void repaintItem(not_null<const ItemBase*> item) = 0;
virtual bool itemVisible(not_null<const ItemBase*> item) = 0; virtual bool itemVisible(not_null<const ItemBase*> item) = 0;
[[nodiscard]] virtual not_null<StickerPremiumMark*> hiddenMark() = 0;
virtual void openPhoto(not_null<PhotoData*> photo, FullMsgId id) = 0; virtual void openPhoto(not_null<PhotoData*> photo, FullMsgId id) = 0;
virtual void openDocument( virtual void openDocument(
not_null<DocumentData*> document, not_null<DocumentData*> document,