feat: suggest ghost mode before opening story

This commit is contained in:
AlexeyZavar
2026-04-25 02:06:00 +03:00
parent fb9816791e
commit ecef927e95
7 changed files with 111 additions and 0 deletions
@@ -115,6 +115,12 @@ void GhostModeAccountSettings::setSendWithoutSound(bool val) {
AyuSettings::save(); AyuSettings::save();
} }
void GhostModeAccountSettings::setSuggestGhostModeBeforeViewingStory(bool val) {
if (_suggestGhostModeBeforeViewingStory.current() == val) return;
_suggestGhostModeBeforeViewingStory = val;
AyuSettings::save();
}
void GhostModeAccountSettings::setGhostModeEnabled(bool val) { void GhostModeAccountSettings::setGhostModeEnabled(bool val) {
if (!_sendReadMessagesLocked.current()) _sendReadMessages = !val; if (!_sendReadMessagesLocked.current()) _sendReadMessages = !val;
if (!_sendReadStoriesLocked.current()) _sendReadStories = !val; if (!_sendReadStoriesLocked.current()) _sendReadStories = !val;
@@ -172,6 +178,7 @@ void to_json(nlohmann::json &j, const GhostModeAccountSettings &s) {
{"markReadAfterAction", s._markReadAfterAction.current()}, {"markReadAfterAction", s._markReadAfterAction.current()},
{"useScheduledMessages", s._useScheduledMessages.current()}, {"useScheduledMessages", s._useScheduledMessages.current()},
{"sendWithoutSound", s._sendWithoutSound.current()}, {"sendWithoutSound", s._sendWithoutSound.current()},
{"suggestGhostModeBeforeViewingStory", s._suggestGhostModeBeforeViewingStory.current()},
{"sendReadMessagesLocked", s._sendReadMessagesLocked.current()}, {"sendReadMessagesLocked", s._sendReadMessagesLocked.current()},
{"sendReadStoriesLocked", s._sendReadStoriesLocked.current()}, {"sendReadStoriesLocked", s._sendReadStoriesLocked.current()},
{"sendOnlinePacketsLocked", s._sendOnlinePacketsLocked.current()}, {"sendOnlinePacketsLocked", s._sendOnlinePacketsLocked.current()},
@@ -189,6 +196,7 @@ void from_json(const nlohmann::json &j, GhostModeAccountSettings &s) {
s._markReadAfterAction = j.value("markReadAfterAction", true); s._markReadAfterAction = j.value("markReadAfterAction", true);
s._useScheduledMessages = j.value("useScheduledMessages", false); s._useScheduledMessages = j.value("useScheduledMessages", false);
s._sendWithoutSound = j.value("sendWithoutSound", false); s._sendWithoutSound = j.value("sendWithoutSound", false);
s._suggestGhostModeBeforeViewingStory = j.value("suggestGhostModeBeforeViewingStory", true);
s._sendReadMessagesLocked = j.value("sendReadMessagesLocked", false); s._sendReadMessagesLocked = j.value("sendReadMessagesLocked", false);
s._sendReadStoriesLocked = j.value("sendReadStoriesLocked", false); s._sendReadStoriesLocked = j.value("sendReadStoriesLocked", false);
s._sendOnlinePacketsLocked = j.value("sendOnlinePacketsLocked", false); s._sendOnlinePacketsLocked = j.value("sendOnlinePacketsLocked", false);
+5
View File
@@ -82,6 +82,7 @@ public:
[[nodiscard]] bool markReadAfterAction() const { return _markReadAfterAction.current(); } [[nodiscard]] bool markReadAfterAction() const { return _markReadAfterAction.current(); }
[[nodiscard]] bool useScheduledMessages() const { return _useScheduledMessages.current(); } [[nodiscard]] bool useScheduledMessages() const { return _useScheduledMessages.current(); }
[[nodiscard]] bool sendWithoutSound() const { return _sendWithoutSound.current(); } [[nodiscard]] bool sendWithoutSound() const { return _sendWithoutSound.current(); }
[[nodiscard]] bool suggestGhostModeBeforeViewingStory() const { return _suggestGhostModeBeforeViewingStory.current(); }
[[nodiscard]] bool isGhostModeActive() const { return _ghostModeActive.current(); } [[nodiscard]] bool isGhostModeActive() const { return _ghostModeActive.current(); }
[[nodiscard]] bool isUseScheduledMessages() const { return isGhostModeActive() && useScheduledMessages(); } [[nodiscard]] bool isUseScheduledMessages() const { return isGhostModeActive() && useScheduledMessages(); }
@@ -99,6 +100,7 @@ public:
void setMarkReadAfterAction(bool val); void setMarkReadAfterAction(bool val);
void setUseScheduledMessages(bool val); void setUseScheduledMessages(bool val);
void setSendWithoutSound(bool val); void setSendWithoutSound(bool val);
void setSuggestGhostModeBeforeViewingStory(bool val);
void setGhostModeEnabled(bool val); void setGhostModeEnabled(bool val);
void setSendReadMessagesLocked(bool val); void setSendReadMessagesLocked(bool val);
@@ -123,6 +125,8 @@ public:
[[nodiscard]] rpl::producer<bool> useScheduledMessagesChanges() const { return _useScheduledMessages.changes(); } [[nodiscard]] rpl::producer<bool> useScheduledMessagesChanges() const { return _useScheduledMessages.changes(); }
[[nodiscard]] rpl::producer<bool> sendWithoutSoundValue() const { return _sendWithoutSound.value(); } [[nodiscard]] rpl::producer<bool> sendWithoutSoundValue() const { return _sendWithoutSound.value(); }
[[nodiscard]] rpl::producer<bool> sendWithoutSoundChanges() const { return _sendWithoutSound.changes(); } [[nodiscard]] rpl::producer<bool> sendWithoutSoundChanges() const { return _sendWithoutSound.changes(); }
[[nodiscard]] rpl::producer<bool> suggestGhostModeBeforeViewingStoryValue() const { return _suggestGhostModeBeforeViewingStory.value(); }
[[nodiscard]] rpl::producer<bool> suggestGhostModeBeforeViewingStoryChanges() const { return _suggestGhostModeBeforeViewingStory.changes(); }
[[nodiscard]] rpl::producer<bool> ghostModeActiveValue() const { return _ghostModeActive.value(); } [[nodiscard]] rpl::producer<bool> ghostModeActiveValue() const { return _ghostModeActive.value(); }
[[nodiscard]] rpl::producer<bool> ghostModeActiveChanges() const { return _ghostModeActive.changes(); } [[nodiscard]] rpl::producer<bool> ghostModeActiveChanges() const { return _ghostModeActive.changes(); }
@@ -151,6 +155,7 @@ private:
rpl::variable<bool> _markReadAfterAction = true; rpl::variable<bool> _markReadAfterAction = true;
rpl::variable<bool> _useScheduledMessages = false; rpl::variable<bool> _useScheduledMessages = false;
rpl::variable<bool> _sendWithoutSound = false; rpl::variable<bool> _sendWithoutSound = false;
rpl::variable<bool> _suggestGhostModeBeforeViewingStory = true;
rpl::variable<bool> _ghostModeActive = false; rpl::variable<bool> _ghostModeActive = false;
rpl::variable<bool> _sendReadMessagesLocked = false; rpl::variable<bool> _sendReadMessagesLocked = false;
+17
View File
@@ -6,9 +6,12 @@
// Copyright @Radolyn, 2026 // Copyright @Radolyn, 2026
#include "ayu/ayu_state.h" #include "ayu/ayu_state.h"
#include "ayu/ayu_settings.h"
namespace AyuState { namespace AyuState {
std::unordered_map<PeerId, std::unordered_set<MsgId>> hiddenMessages; std::unordered_map<PeerId, std::unordered_set<MsgId>> hiddenMessages;
Main::Session *disableGhostModeOnStoryCloseSession = nullptr;
void hide(PeerId peerId, MsgId messageId) { void hide(PeerId peerId, MsgId messageId) {
hiddenMessages[peerId].insert(messageId); hiddenMessages[peerId].insert(messageId);
@@ -30,4 +33,18 @@ bool isHidden(not_null<HistoryItem*> item) {
return isHidden(item->history()->peer->id, item->id); return isHidden(item->history()->peer->id, item->id);
} }
void setDisableGhostModeOnStoryClose(Main::Session *session) {
disableGhostModeOnStoryCloseSession = session;
}
void disableGhostModeOnStoryClose(Main::Session *session) {
if (disableGhostModeOnStoryCloseSession != session) {
return;
}
disableGhostModeOnStoryCloseSession = nullptr;
if (session) {
AyuSettings::ghost(session).setGhostModeEnabled(false);
}
}
} }
+7
View File
@@ -9,6 +9,10 @@
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
namespace Main {
class Session;
} // namespace Main
namespace AyuState { namespace AyuState {
void hide(PeerId peerId, MsgId messageId); void hide(PeerId peerId, MsgId messageId);
@@ -16,4 +20,7 @@ void hide(not_null<HistoryItem*> item);
bool isHidden(PeerId peerId, MsgId messageId); bool isHidden(PeerId peerId, MsgId messageId);
bool isHidden(not_null<HistoryItem*> item); bool isHidden(not_null<HistoryItem*> item);
void setDisableGhostModeOnStoryClose(Main::Session *session);
void disableGhostModeOnStoryClose(Main::Session *session);
} }
@@ -276,6 +276,7 @@ void BuildGhostEssentials(SectionBuilder &builder) {
dst.setMarkReadAfterAction(src.markReadAfterAction()); dst.setMarkReadAfterAction(src.markReadAfterAction());
dst.setUseScheduledMessages(src.useScheduledMessages()); dst.setUseScheduledMessages(src.useScheduledMessages());
dst.setSendWithoutSound(src.sendWithoutSound()); dst.setSendWithoutSound(src.sendWithoutSound());
dst.setSuggestGhostModeBeforeViewingStory(src.suggestGhostModeBeforeViewingStory());
dst.setSendReadMessagesLocked(src.sendReadMessagesLocked()); dst.setSendReadMessagesLocked(src.sendReadMessagesLocked());
dst.setSendReadStoriesLocked(src.sendReadStoriesLocked()); dst.setSendReadStoriesLocked(src.sendReadStoriesLocked());
dst.setSendOnlinePacketsLocked(src.sendOnlinePacketsLocked()); dst.setSendOnlinePacketsLocked(src.sendOnlinePacketsLocked());
@@ -472,6 +473,34 @@ void BuildGhostEssentials(SectionBuilder &builder) {
AddSkip(container); AddSkip(container);
AddDividerText(container, tr::ayu_SendWithoutSoundByDefaultDescription()); AddDividerText(container, tr::ayu_SendWithoutSoundByDefaultDescription());
AddSkip(container);
const auto suggestGhostModeButton = AddButtonWithIcon(
container,
tr::ayu_SuggestGhostModeBeforeViewingStory(),
st::settingsButtonNoIcon);
if (wctx.highlights) {
wctx.highlights->push_back(std::make_pair(
u"ayu/suggestGhostModeBeforeViewingStory"_q,
HighlightEntry{ suggestGhostModeButton.get(), {} }));
}
suggestGhostModeButton->toggleOn(
state->selectedUserId.value()
| rpl::map([](uint64 id) {
return AyuSettings::ghost(id).suggestGhostModeBeforeViewingStoryValue();
}) | rpl::flatten_latest()
)->toggledValue(
) | rpl::filter(
[=](bool enabled) {
return enabled != AyuSettings::ghost(state->selectedUserId.current()).suggestGhostModeBeforeViewingStory();
}
) | on_next(
[=](bool enabled) {
AyuSettings::ghost(state->selectedUserId.current()).setSuggestGhostModeBeforeViewingStory(enabled);
},
container->lifetime());
AddSkip(container);
AddDividerText(container, tr::ayu_SuggestGhostModeBeforeViewingStoryDescription());
auto showMenu = [=] { auto showMenu = [=] {
state->menu = base::make_unique_q<Ui::PopupMenu>( state->menu = base::make_unique_q<Ui::PopupMenu>(
pickerButton, pickerButton,
@@ -525,6 +554,11 @@ void BuildGhostEssentials(SectionBuilder &builder) {
.title = tr::ayu_SendWithoutSoundByDefault(tr::now), .title = tr::ayu_SendWithoutSoundByDefault(tr::now),
.section = sctx.sectionId, .section = sctx.sectionId,
}); });
sctx.entries->push_back({
.id = u"ayu/suggestGhostModeBeforeViewingStory"_q,
.title = tr::ayu_SuggestGhostModeBeforeViewingStory(tr::now),
.section = sctx.sectionId,
});
}); });
}); });
} }
@@ -119,6 +119,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <kurlmimedata.h> #include <kurlmimedata.h>
// AyuGram includes // AyuGram includes
#include "ayu/ayu_state.h"
#include "ayu/features/streamer_mode/streamer_mode.h" #include "ayu/features/streamer_mode/streamer_mode.h"
@@ -7933,6 +7934,7 @@ Window::SessionController *OverlayWidget::findWindow(bool switchTo) const {
// #TODO unite and check // #TODO unite and check
void OverlayWidget::clearBeforeHide() { void OverlayWidget::clearBeforeHide() {
AyuState::disableGhostModeOnStoryClose(_storiesSession);
_message = nullptr; _message = nullptr;
_sharedMedia = nullptr; _sharedMedia = nullptr;
_sharedMediaData = std::nullopt; _sharedMediaData = std::nullopt;
@@ -37,6 +37,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <QtGui/QScreen> #include <QtGui/QScreen>
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/ayu_state.h"
#include "data/data_story.h"
namespace Window { namespace Window {
namespace { namespace {
@@ -567,6 +573,38 @@ Window::Adaptive &Controller::adaptive() const {
} }
void Controller::openInMediaView(Media::View::OpenRequest &&request) { void Controller::openInMediaView(Media::View::OpenRequest &&request) {
if (request.story()) {
const auto story = not_null{ request.story() };
auto &ghost = AyuSettings::ghost(&story->session());
const auto suggestGhostMode = ghost.suggestGhostModeBeforeViewingStory()
&& ghost.sendReadStories()
&& !ghost.sendReadStoriesLocked()
&& !ghost.isGhostModeActive();
if (suggestGhostMode) {
const auto controller = request.controller();
const auto context = request.storiesContext();
show(Ui::MakeConfirmBox({
.text = tr::ayu_SuggestGhostModeStoryText(tr::now, tr::rich),
.confirmed = [=](Fn<void()> close) {
close();
AyuSettings::ghost(&story->session()).setGhostModeEnabled(true);
AyuState::setDisableGhostModeOnStoryClose(&story->session());
_openInMediaViewRequests.fire(
Media::View::OpenRequest(controller, story, context));
},
.cancelled = [=](Fn<void()> close) {
close();
_openInMediaViewRequests.fire(
Media::View::OpenRequest(controller, story, context));
},
.confirmText = tr::ayu_SuggestGhostModeStoryActionTextYes(),
.cancelText = tr::ayu_SuggestGhostModeStoryActionTextNo(),
.title = tr::ayu_SuggestGhostModeTitle(),
.strictCancel = true,
}));
return;
}
}
_openInMediaViewRequests.fire(std::move(request)); _openInMediaViewRequests.fire(std::move(request));
} }