mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 15:17:07 +02:00
Save to Profile / Archive stories from the viewer.
This commit is contained in:
parent
6c960243a9
commit
af0e578da5
14 changed files with 203 additions and 3 deletions
Telegram
|
@ -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.";
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -172,6 +172,7 @@ void InnerWidget::createButtons() {
|
|||
return !content.elements.empty();
|
||||
}),
|
||||
[] { return st::dialogsStories.height; });
|
||||
thumbs->show();
|
||||
rpl::combine(
|
||||
recent->sizeValue(),
|
||||
rpl::duplicate(last)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -841,3 +841,6 @@ storiesReportBox: ReportBox(defaultReportBox) {
|
|||
personal: icon {{ "menu/personal", storiesComposeWhiteText }};
|
||||
other: icon {{ "menu/report", storiesComposeWhiteText }};
|
||||
}
|
||||
storiesActionToast: Toast(defaultToast) {
|
||||
maxWidth: 320px;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue