Save to Profile / Archive stories from the viewer.

This commit is contained in:
John Preston 2023-06-30 15:24:59 +04:00
parent 6c960243a9
commit af0e578da5
14 changed files with 203 additions and 3 deletions

View File

@ -2477,6 +2477,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mediaview_copy" = "Copy";
"lng_mediaview_forward" = "Forward";
"lng_mediaview_delete" = "Delete";
"lng_mediaview_save_to_profile" = "Save to Profile";
"lng_mediaview_archive_story" = "Archive Story";
"lng_mediaview_photos_all" = "View all photos";
"lng_mediaview_files_all" = "View all files";
"lng_mediaview_single_photo" = "Single Photo";
@ -3837,6 +3839,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_delete_one_sure" = "Are you sure you want to delete this story?";
"lng_stories_delete_sure#one" = "Are you sure you want to delete {count} story?";
"lng_stories_delete_sure#other" = "Are you sure you want to delete {count} stories?";
"lng_stories_save_sure" = "Do you want to save this story to your profile?";
"lng_stories_save_sure_many#one" = "Do you want to save {count} story to your profile?";
"lng_stories_save_sure_many#other" = "Do you want to save {count} stories to your profile?";
"lng_stories_save_done" = "This story is saved to your profile.";
"lng_stories_save_done_many#one" = "{count} story is saved to your profile.";
"lng_stories_save_done_many#other" = "{count} stories are saved to your profile.";
"lng_stories_save_done_about" = "Saved stories can be viewed by others on your profile until you remove them.";
"lng_stories_archive_sure" = "Do you want to hide this story from your profile?";
"lng_stories_archive_sure_many#one" = "Do you want to hide {count} story from your profile?";
"lng_stories_archive_sure_many#other" = "Do you want to hide {count} stories from your profile?";
"lng_stories_archive_done" = "This story is hidden from your profile.";
"lng_stories_archive_done_many#one" = "{count} story is hidden from your profile.";
"lng_stories_archive_done_many#other" = "{count} stories are hidden from your profile.";
"lng_stories_link_invalid" = "This link is broken or has expired.";

View File

@ -1275,6 +1275,63 @@ void Stories::deleteList(const std::vector<FullStoryId> &ids) {
}
}
void Stories::togglePinnedList(
const std::vector<FullStoryId> &ids,
bool pinned) {
auto list = QVector<MTPint>();
list.reserve(ids.size());
const auto selfId = session().userPeerId();
for (const auto &id : ids) {
if (id.peer == selfId) {
list.push_back(MTP_int(id.story));
}
}
if (list.empty()) {
return;
}
const auto api = &_owner->session().api();
api->request(MTPstories_TogglePinned(
MTP_vector<MTPint>(list),
MTP_bool(pinned)
)).done([=](const MTPVector<MTPint> &result) {
auto &saved = _saved[selfId];
const auto loaded = saved.loaded;
const auto lastId = !saved.ids.list.empty()
? saved.ids.list.back()
: std::numeric_limits<StoryId>::max();
auto dirty = false;
for (const auto &id : result.v) {
if (const auto maybeStory = lookup({ selfId, id.v })) {
const auto story = *maybeStory;
story->setPinned(pinned);
if (pinned) {
const auto add = loaded || (id.v >= lastId);
if (!add) {
dirty = true;
} else if (saved.ids.list.emplace(id.v).second) {
if (saved.total >= 0) {
++saved.total;
}
}
} else if (saved.ids.list.remove(id.v)) {
if (saved.total > 0) {
--saved.total;
}
} else if (!loaded) {
dirty = true;
}
} else if (!loaded) {
dirty = true;
}
}
if (dirty) {
savedLoadMore(selfId);
} else {
_savedChanged.fire_copy(selfId);
}
}).send();
}
void Stories::report(
std::shared_ptr<Ui::Show> show,
FullStoryId id,

View File

@ -121,6 +121,8 @@ public:
explicit Stories(not_null<Session*> owner);
~Stories();
static constexpr auto kPinnedToastDuration = 4 * crl::time(1000);
[[nodiscard]] Session &owner() const;
[[nodiscard]] Main::Session &session() const;
@ -183,6 +185,7 @@ public:
void savedLoadMore(PeerId peerId);
void deleteList(const std::vector<FullStoryId> &ids);
void togglePinnedList(const std::vector<FullStoryId> &ids, bool pinned);
void report(
std::shared_ptr<Ui::Show> show,
FullStoryId id,

View File

@ -28,6 +28,25 @@ InfoPeerBadge {
sizeTag: int;
}
InfoTopBar {
height: pixels;
back: IconButton;
title: FlatLabel;
titlePosition: point;
bg: color;
mediaCancel: IconButton;
mediaActionsSkip: pixels;
mediaForward: IconButton;
mediaDelete: IconButton;
storiesSave: IconButton;
storiesArchive: IconButton;
search: IconButton;
searchRow: SearchFieldRow;
highlightBg: color;
highlightDuration: int;
radius: pixels;
}
infoMediaHeaderFg: windowFg;
infoToggle: InfoToggle {
@ -156,6 +175,14 @@ infoTopBarDelete: IconButton(infoTopBarForward) {
icon: icon {{ "info/info_media_delete", boxTitleCloseFg }};
iconOver: icon {{ "info/info_media_delete", boxTitleCloseFgOver }};
}
infoTopBarSaveStories: IconButton(infoTopBarForward) {
icon: icon {{ "menu/stories_saved", boxTitleCloseFg }};
iconOver: icon {{ "menu/stories_saved", boxTitleCloseFgOver }};
}
infoTopBarArchiveStories: IconButton(infoTopBarForward) {
icon: icon {{ "menu/archive", boxTitleCloseFg }};
iconOver: icon {{ "menu/archive", boxTitleCloseFgOver }};
}
infoTopBar: InfoTopBar {
height: infoTopBarHeight;
back: infoTopBarBack;
@ -166,6 +193,8 @@ infoTopBar: InfoTopBar {
mediaActionsSkip: 4px;
mediaForward: infoTopBarForward;
mediaDelete: infoTopBarDelete;
storiesSave: infoTopBarSaveStories;
storiesArchive: infoTopBarArchiveStories;
search: infoTopBarSearch;
searchRow: infoTopBarSearchRow;
highlightBg: windowBgOver;
@ -221,6 +250,14 @@ infoLayerTopBarDelete: IconButton(infoLayerTopBarForward) {
icon: icon {{ "info/info_media_delete", boxTitleCloseFg }};
iconOver: icon {{ "info/info_media_delete", boxTitleCloseFgOver }};
}
infoLayerTopBarSaveStories: IconButton(infoLayerTopBarForward) {
icon: icon {{ "menu/stories_saved", boxTitleCloseFg }};
iconOver: icon {{ "menu/stories_saved", boxTitleCloseFgOver }};
}
infoLayerTopBarArchiveStories: IconButton(infoLayerTopBarForward) {
icon: icon {{ "menu/archive", boxTitleCloseFg }};
iconOver: icon {{ "menu/archive", boxTitleCloseFgOver }};
}
infoLayerTopBar: InfoTopBar(infoTopBar) {
height: infoLayerTopBarHeight;
back: infoLayerTopBarBack;
@ -231,6 +268,8 @@ infoLayerTopBar: InfoTopBar(infoTopBar) {
mediaActionsSkip: 6px;
mediaForward: infoLayerTopBarForward;
mediaDelete: infoLayerTopBarDelete;
storiesSave: infoLayerTopBarSaveStories;
storiesArchive: infoLayerTopBarArchiveStories;
search: infoTopBarSearch;
searchRow: infoTopBarSearchRow;
radius: boxRadius;

View File

@ -172,6 +172,7 @@ void InnerWidget::createButtons() {
return !content.elements.empty();
}),
[] { return st::dialogsStories.height; });
thumbs->show();
rpl::combine(
recent->sizeValue(),
rpl::duplicate(last)

View File

@ -39,6 +39,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/message_sending_animation_common.h"
#include "ui/effects/reaction_fly_animation.h"
#include "ui/layers/box_content.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/round_rect.h"
@ -1254,8 +1256,16 @@ void Controller::deleteRequested() {
return;
}
const auto id = story->fullId();
const auto weak = base::make_weak(this);
const auto owner = &story->owner();
const auto confirmed = [=](Fn<void()> close) {
if (const auto strong = weak.get()) {
if (const auto story = strong->story()) {
if (story->fullId() == id) {
moveFromShown();
}
}
}
owner->stories().deleteList({ id });
close();
};
@ -1290,6 +1300,38 @@ void Controller::reportRequested() {
}));
}
void Controller::togglePinnedRequested(bool pinned) {
const auto story = this->story();
if (!story || !story->peer()->isSelf()) {
return;
}
if (!pinned && v::is<Data::StoriesContextSaved>(_context.data)) {
moveFromShown();
}
story->owner().stories().togglePinnedList({ story->fullId() }, pinned);
uiShow()->showToast({
.text = (pinned
? tr::lng_stories_save_done(
tr::now,
Ui::Text::Bold).append(
'\n').append(
tr::lng_stories_save_done_about(tr::now))
: tr::lng_stories_archive_done(
tr::now,
Ui::Text::WithEntities)),
.st = &st::storiesActionToast,
.duration = (pinned
? Data::Stories::kPinnedToastDuration
: Ui::Toast::kDefaultDuration),
});
}
void Controller::moveFromShown() {
if (!subjumpFor(1)) {
[[maybe_unused]] const auto jumped = subjumpFor(-1);
}
}
rpl::lifetime &Controller::lifetime() {
return _lifetime;
}

View File

@ -91,7 +91,7 @@ struct ViewsSlice {
int left = 0;
};
class Controller final {
class Controller final : public base::has_weak_ptr {
public:
explicit Controller(not_null<Delegate*> delegate);
~Controller();
@ -135,6 +135,7 @@ public:
void shareRequested();
void deleteRequested();
void reportRequested();
void togglePinnedRequested(bool pinned);
[[nodiscard]] rpl::lifetime &lifetime();
@ -171,6 +172,7 @@ private:
void subjumpTo(int index);
void checkWaitingFor();
void moveFromShown();
void refreshViewsFromData();
bool sliceViewsTo(PeerId offset);

View File

@ -83,6 +83,10 @@ void View::contentPressed(bool pressed) {
_controller->contentPressed(pressed);
}
void View::menuShown(bool shown) {
_controller->setMenuShown(shown);
}
void View::shareRequested() {
_controller->shareRequested();
}
@ -95,6 +99,10 @@ void View::reportRequested() {
_controller->reportRequested();
}
void View::togglePinnedRequested(bool pinned) {
_controller->togglePinnedRequested(pinned);
}
SiblingView View::sibling(SiblingType type) const {
return _controller->sibling(type);
}

View File

@ -75,10 +75,12 @@ public:
[[nodiscard]] bool paused() const;
void togglePaused(bool paused);
void contentPressed(bool pressed);
void menuShown(bool shown);
void shareRequested();
void deleteRequested();
void reportRequested();
void togglePinnedRequested(bool pinned);
[[nodiscard]] rpl::lifetime &lifetime();

View File

@ -841,3 +841,6 @@ storiesReportBox: ReportBox(defaultReportBox) {
personal: icon {{ "menu/personal", storiesComposeWhiteText }};
other: icon {{ "menu/report", storiesComposeWhiteText }};
}
storiesActionToast: Toast(defaultToast) {
maxWidth: 320px;
}

View File

@ -1408,6 +1408,19 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) {
[=] { toMessage(); },
&st::mediaMenuIconShowInChat);
}
if (story && story->peer()->isSelf()) {
const auto pinned = story->pinned();
const auto text = pinned
? tr::lng_mediaview_archive_story(tr::now)
: tr::lng_mediaview_save_to_profile(tr::now);
addAction(text, [=] {
if (_stories) {
_stories->togglePinnedRequested(!pinned);
}
}, pinned
? &st::mediaMenuIconArchiveStory
: &st::mediaMenuIconSaveStory);
}
if ((!story || story->canDownload())
&& _document
&& !_document->filepath(true).isEmpty()) {
@ -2156,6 +2169,9 @@ void OverlayWidget::hideControls(bool force) {
void OverlayWidget::dropdownHidden() {
setFocus();
if (_stories) {
_stories->menuShown(false);
}
_ignoringDropdown = true;
_lastMouseMovePos = _widget->mapFromGlobal(QCursor::pos());
updateOver(_lastMouseMovePos);
@ -5387,7 +5403,7 @@ void OverlayWidget::updateOverRect(Over state) {
bool OverlayWidget::updateOverState(Over newState) {
bool result = true;
if (_over != newState) {
if (newState == Over::More && !_ignoringDropdown) {
if (!_stories && newState == Over::More && !_ignoringDropdown) {
_dropdownShowTimer.callOnce(0);
} else {
_dropdownShowTimer.cancel();
@ -5637,7 +5653,13 @@ bool OverlayWidget::handleContextMenu(std::optional<QPoint> position) {
if (_menu->empty()) {
_menu = nullptr;
} else {
if (_stories) {
_stories->menuShown(true);
}
_menu->setDestroyedCallback(crl::guard(_widget, [=] {
if (_stories) {
_stories->menuShown(false);
}
activateControls();
_receiveMouse = false;
InvokeQueued(_widget, [=] { receiveMouse(); });
@ -5905,6 +5927,9 @@ void OverlayWidget::showDropdown() {
_dropdown->moveToRight(0, height() - _dropdown->height());
_dropdown->showAnimated(Ui::PanelAnimation::Origin::BottomRight);
_dropdown->setFocus();
if (_stories) {
_stories->menuShown(true);
}
}
void OverlayWidget::handleTouchTimer() {

View File

@ -219,6 +219,7 @@ void CreateRightLabel(
const auto name = Ui::CreateChild<Ui::FlatLabel>(
button.get(),
st.rightLabel);
name->show();
rpl::combine(
button->widthValue(),
std::move(buttonText),

View File

@ -126,6 +126,8 @@ mediaMenuIconDelete: icon {{ "menu/delete", mediaviewMenuFg }};
mediaMenuIconShowAll: icon {{ "menu/all_media", mediaviewMenuFg }};
mediaMenuIconProfile: icon {{ "menu/profile", mediaviewMenuFg }};
mediaMenuIconReport: icon {{ "menu/report", mediaviewMenuFg }};
mediaMenuIconSaveStory: icon {{ "menu/stories_saved", mediaviewMenuFg }};
mediaMenuIconArchiveStory: icon {{ "menu/archive", mediaviewMenuFg }};
menuIconDeleteAttention: icon {{ "menu/delete", menuIconAttentionColor }};
menuIconLeaveAttention: icon {{ "menu/leave", menuIconAttentionColor }};

@ -1 +1 @@
Subproject commit 08f80548668df2737905365b254624055642b937
Subproject commit 67dc933d72fa5e20e6480bbff5a88a2c52d9d0d0