feat: support send without sound in ghost mode

This commit is contained in:
AlexeyZavar
2026-04-25 05:04:50 +03:00
parent cc56552cb0
commit 44ffd2aea1
6 changed files with 76 additions and 43 deletions
+22 -2
View File
@@ -109,7 +109,20 @@ void GhostModeAccountSettings::setUseScheduledMessages(bool val) {
AyuSettings::save();
}
void GhostModeAccountSettings::setSendWithoutSound(bool val) {
bool GhostModeAccountSettings::shouldSendWithoutSound() const {
switch (_sendWithoutSound.current()) {
case SendWithoutSoundOption::Never:
return false;
case SendWithoutSoundOption::InGhostMode:
return isGhostModeActive();
case SendWithoutSoundOption::Always:
return true;
}
Unexpected("Value in GhostModeAccountSettings::shouldSendWithoutSound.");
}
void GhostModeAccountSettings::setSendWithoutSound(
SendWithoutSoundOption val) {
if (_sendWithoutSound.current() == val) return;
_sendWithoutSound = val;
AyuSettings::save();
@@ -195,7 +208,14 @@ void from_json(const nlohmann::json &j, GhostModeAccountSettings &s) {
s._sendOfflinePacketAfterOnline = j.value("sendOfflinePacketAfterOnline", false);
s._markReadAfterAction = j.value("markReadAfterAction", true);
s._useScheduledMessages = j.value("useScheduledMessages", false);
s._sendWithoutSound = j.value("sendWithoutSound", false);
const auto sendWithoutSound = j.find("sendWithoutSound");
s._sendWithoutSound = (sendWithoutSound == j.end())
? SendWithoutSoundOption::Never
: sendWithoutSound->is_boolean()
? (sendWithoutSound->get<bool>()
? SendWithoutSoundOption::Always
: SendWithoutSoundOption::Never)
: sendWithoutSound->get<SendWithoutSoundOption>();
s._suggestGhostModeBeforeViewingStory = j.value("suggestGhostModeBeforeViewingStory", true);
s._sendReadMessagesLocked = j.value("sendReadMessagesLocked", false);
s._sendReadStoriesLocked = j.value("sendReadStoriesLocked", false);
+18 -5
View File
@@ -45,6 +45,12 @@ enum class TranslationProvider {
Native = 3,
};
enum class SendWithoutSoundOption {
Never = 0,
InGhostMode = 1,
Always = 2,
};
NLOHMANN_JSON_SERIALIZE_ENUM(PeerIdDisplay, {
{PeerIdDisplay::Hidden, 0},
{PeerIdDisplay::TelegramApi, 1},
@@ -70,6 +76,12 @@ NLOHMANN_JSON_SERIALIZE_ENUM(TranslationProvider, {
{TranslationProvider::Native, "native"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(SendWithoutSoundOption, {
{SendWithoutSoundOption::Never, 0},
{SendWithoutSoundOption::InGhostMode, 1},
{SendWithoutSoundOption::Always, 2},
})
class GhostModeAccountSettings {
public:
GhostModeAccountSettings();
@@ -81,7 +93,8 @@ public:
[[nodiscard]] bool sendOfflinePacketAfterOnline() const { return _sendOfflinePacketAfterOnline.current(); }
[[nodiscard]] bool markReadAfterAction() const { return _markReadAfterAction.current(); }
[[nodiscard]] bool useScheduledMessages() const { return _useScheduledMessages.current(); }
[[nodiscard]] bool sendWithoutSound() const { return _sendWithoutSound.current(); }
[[nodiscard]] SendWithoutSoundOption sendWithoutSound() const { return _sendWithoutSound.current(); }
[[nodiscard]] bool shouldSendWithoutSound() const;
[[nodiscard]] bool suggestGhostModeBeforeViewingStory() const { return _suggestGhostModeBeforeViewingStory.current(); }
[[nodiscard]] bool isGhostModeActive() const { return _ghostModeActive.current(); }
[[nodiscard]] bool isUseScheduledMessages() const { return isGhostModeActive() && useScheduledMessages(); }
@@ -99,7 +112,7 @@ public:
void setSendOfflinePacketAfterOnline(bool val);
void setMarkReadAfterAction(bool val);
void setUseScheduledMessages(bool val);
void setSendWithoutSound(bool val);
void setSendWithoutSound(SendWithoutSoundOption val);
void setSuggestGhostModeBeforeViewingStory(bool val);
void setGhostModeEnabled(bool val);
@@ -123,8 +136,8 @@ public:
[[nodiscard]] rpl::producer<bool> markReadAfterActionChanges() const { return _markReadAfterAction.changes(); }
[[nodiscard]] rpl::producer<bool> useScheduledMessagesValue() const { return _useScheduledMessages.value(); }
[[nodiscard]] rpl::producer<bool> useScheduledMessagesChanges() const { return _useScheduledMessages.changes(); }
[[nodiscard]] rpl::producer<bool> sendWithoutSoundValue() const { return _sendWithoutSound.value(); }
[[nodiscard]] rpl::producer<bool> sendWithoutSoundChanges() const { return _sendWithoutSound.changes(); }
[[nodiscard]] rpl::producer<SendWithoutSoundOption> sendWithoutSoundValue() const { return _sendWithoutSound.value(); }
[[nodiscard]] rpl::producer<SendWithoutSoundOption> 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(); }
@@ -154,7 +167,7 @@ private:
rpl::variable<bool> _sendOfflinePacketAfterOnline = false;
rpl::variable<bool> _markReadAfterAction = true;
rpl::variable<bool> _useScheduledMessages = false;
rpl::variable<bool> _sendWithoutSound = false;
rpl::variable<SendWithoutSoundOption> _sendWithoutSound = SendWithoutSoundOption::Never;
rpl::variable<bool> _suggestGhostModeBeforeViewingStory = true;
rpl::variable<bool> _ghostModeActive = false;
@@ -27,6 +27,7 @@
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"
#include "ui/painter.h"
#include "ui/boxes/single_choice_box.h"
#include "ui/vertical_list.h"
#include "ui/text/text.h"
#include "ui/toast/toast.h"
@@ -445,31 +446,45 @@ void BuildGhostEssentials(SectionBuilder &builder) {
AddDividerText(container, tr::ayu_UseScheduledMessagesDescription());
AddSkip(container);
const auto silentButton = AddButtonWithIcon(
const auto silentOptions = std::vector<QString>{
tr::ayu_SendWithoutSoundByDefaultNever(tr::now),
tr::ayu_SendWithoutSoundByDefaultInGhostMode(tr::now),
tr::ayu_SendWithoutSoundByDefaultAlways(tr::now),
};
const auto silentOptionText = state->selectedUserId.value(
) | rpl::map([=](uint64 id) {
return AyuSettings::ghost(id).sendWithoutSoundValue(
) | rpl::map([=](SendWithoutSoundOption value) {
return silentOptions[static_cast<int>(value)];
});
}) | rpl::flatten_latest();
const auto silentButton = AddButtonWithLabel(
container,
tr::ayu_SendWithoutSoundByDefault(),
st::settingsButtonNoIcon
);
std::move(silentOptionText),
st::settingsButtonNoIcon);
if (wctx.highlights) {
wctx.highlights->push_back(std::make_pair(
u"ayu/sendWithoutSound"_q,
HighlightEntry{ silentButton.get(), {} }));
}
silentButton->toggleOn(
state->selectedUserId.value()
| rpl::map([](uint64 id) {
return AyuSettings::ghost(id).sendWithoutSoundValue();
}) | rpl::flatten_latest()
)->toggledValue(
) | rpl::filter(
[=](bool enabled) {
return enabled != AyuSettings::ghost(state->selectedUserId.current()).sendWithoutSound();
}
) | on_next(
[=](bool enabled) {
AyuSettings::ghost(state->selectedUserId.current()).setSendWithoutSound(enabled);
},
container->lifetime());
silentButton->addClickHandler([=] {
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
const auto save = [=](int index) {
AyuSettings::ghost(state->selectedUserId.current()
).setSendWithoutSound(
static_cast<SendWithoutSoundOption>(index));
};
SingleChoiceBox(box, {
.title = tr::ayu_SendWithoutSoundByDefault(),
.options = silentOptions,
.initialSelection = static_cast<int>(
AyuSettings::ghost(state->selectedUserId.current()
).sendWithoutSound()),
.callback = save,
});
}));
});
AddSkip(container);
AddDividerText(container, tr::ayu_SendWithoutSoundByDefaultDescription());
@@ -614,7 +614,7 @@ bool ShouldSendSilent(
not_null<PeerData*> peer,
const Api::SendOptions &options) {
const auto &ghost = AyuSettings::ghost(&peer->session());
if (ghost.sendWithoutSound()) {
if (ghost.shouldSendWithoutSound()) {
return !options.silent;
}
+1 -1
View File
@@ -749,7 +749,7 @@ FillMenuResult FillSendMenu(
const auto &ghost = maybeShow
? AyuSettings::ghost(&maybeShow->session())
: AyuSettings::ghost();
const auto sendWithoutSound = ghost.sendWithoutSound();
const auto sendWithoutSound = ghost.shouldSendWithoutSound();
menu->addAction(
sendWithoutSound ? tr::ayu_SendWithSound(tr::now) : tr::lng_send_silent_message(tr::now),
[=] { action({ Api::SendOptions{ .silent = true } }, details); },
@@ -105,7 +105,6 @@ private:
QAction *_ghostMode = nullptr;
QAction *_readOnInteract = nullptr;
QAction *_scheduleMessages = nullptr;
QAction *_sendWithoutSound = nullptr;
NSPasteboard *_pasteboard = nullptr;
int _pasteboardChangeCount = -1;
@@ -237,9 +236,6 @@ void Manager::retranslate() {
if (_scheduleMessages) {
_scheduleMessages->setText(tr::ayu_UseScheduledMessages(tr::now));
}
if (_sendWithoutSound) {
_sendWithoutSound->setText(tr::ayu_SendWithoutSoundByDefault(tr::now));
}
}
void Manager::ensureLanguageBound() {
@@ -336,7 +332,6 @@ void Manager::recomputeState() {
ForceDisabled(_ghostMode, ghostInactive);
ForceDisabled(_readOnInteract, ghostInactive);
ForceDisabled(_scheduleMessages, ghostInactive);
ForceDisabled(_sendWithoutSound, ghostInactive);
const auto setChecked = [](QAction *action, bool checked) {
const auto wasBlocked = action->blockSignals(true);
action->setChecked(checked);
@@ -348,12 +343,10 @@ void Manager::recomputeState() {
: tr::ayu_EnableGhostMode(tr::now));
setChecked(_readOnInteract, ghost->markReadAfterAction());
setChecked(_scheduleMessages, ghost->useScheduledMessages());
setChecked(_sendWithoutSound, ghost->sendWithoutSound());
} else {
_ghostMode->setText(tr::ayu_EnableGhostMode(tr::now));
setChecked(_readOnInteract, false);
setChecked(_scheduleMessages, false);
setChecked(_sendWithoutSound, false);
}
}
@@ -592,13 +585,6 @@ void Manager::buildGhostModeMenu(QMenu *ghostMode) {
}
});
_sendWithoutSound = addToggle(
u"Send without Sound"_q,
[this](bool enabled) {
if (const auto ghost = resolveGhostSettings()) {
ghost->setSendWithoutSound(enabled);
}
});
}
void Manager::buildWindowMenu(QMenu *window) {
@@ -720,8 +706,7 @@ void Manager::destroy() {
= _strikeOut = _blockquote = _monospace = _clearFormat
= nullptr;
_ghostModeMenu = nullptr;
_ghostMode = _readOnInteract = _scheduleMessages = _sendWithoutSound
= nullptr;
_ghostMode = _readOnInteract = _scheduleMessages = nullptr;
_pasteboard = nullptr;
_pasteboardChangeCount = -1;
_pasteboardHasText = false;