mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 05:07:10 +02:00
Support 18+/restrictions for messages.
This commit is contained in:
parent
074dbf41e0
commit
b9de12fedb
35 changed files with 711 additions and 299 deletions
|
@ -3485,6 +3485,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_paid_react_show_in_top" = "Show me in Top Senders";
|
||||
"lng_paid_react_anonymous" = "Anonymous";
|
||||
|
||||
"lng_sensitive_tag" = "18+";
|
||||
"lng_sensitive_title" = "18+";
|
||||
"lng_sensitive_text" = "This media may contain sensitive content suitable only for adults. Do you still want to view it?";
|
||||
"lng_sensitive_always" = "Always show 18+ media";
|
||||
"lng_sensitive_view" = "View Anyway";
|
||||
"lng_sensitive_toast" = "You can update the visibility of sensitive media in **Settings > Chat Settings > Sensitive content**";
|
||||
|
||||
"lng_translate_show_original" = "Show Original";
|
||||
"lng_translate_bar_to" = "Translate to {name}";
|
||||
"lng_translate_bar_to_other" = "Translate to {name}";
|
||||
|
|
|
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kRefreshAppConfigTimeout = 3 * crl::time(1000);
|
||||
constexpr auto kRefreshAppConfigTimeout = crl::time(1);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -24,19 +24,40 @@ SensitiveContent::SensitiveContent(not_null<ApiWrap*> api)
|
|||
, _appConfigReloadTimer([=] { _session->appConfig().refresh(); }) {
|
||||
}
|
||||
|
||||
void SensitiveContent::reload() {
|
||||
if (_requestId) {
|
||||
void SensitiveContent::preload() {
|
||||
if (!_loaded) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
void SensitiveContent::reload(bool force) {
|
||||
if (_loadRequestId) {
|
||||
if (force) {
|
||||
_loadPending = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_requestId = _api.request(MTPaccount_GetContentSettings(
|
||||
_loaded = true;
|
||||
_loadRequestId = _api.request(MTPaccount_GetContentSettings(
|
||||
)).done([=](const MTPaccount_ContentSettings &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDaccount_contentSettings &data) {
|
||||
_enabled = data.is_sensitive_enabled();
|
||||
_canChange = data.is_sensitive_can_change();
|
||||
});
|
||||
_loadRequestId = 0;
|
||||
const auto &data = result.data();
|
||||
const auto enabled = data.is_sensitive_enabled();
|
||||
const auto canChange = data.is_sensitive_can_change();
|
||||
const auto changed = (_enabled.current() != enabled)
|
||||
|| (_canChange.current() != canChange);
|
||||
if (changed) {
|
||||
_enabled = enabled;
|
||||
_canChange = canChange;
|
||||
}
|
||||
if (base::take(_appConfigReloadForce) || changed) {
|
||||
_appConfigReloadTimer.callOnce(kRefreshAppConfigTimeout);
|
||||
}
|
||||
if (base::take(_loadPending)) {
|
||||
reload();
|
||||
}
|
||||
}).fail([=] {
|
||||
_requestId = 0;
|
||||
_loadRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
|
@ -57,17 +78,24 @@ void SensitiveContent::update(bool enabled) {
|
|||
return;
|
||||
}
|
||||
using Flag = MTPaccount_SetContentSettings::Flag;
|
||||
_api.request(_requestId).cancel();
|
||||
_requestId = _api.request(MTPaccount_SetContentSettings(
|
||||
_api.request(_saveRequestId).cancel();
|
||||
if (const auto load = base::take(_loadRequestId)) {
|
||||
_api.request(load).cancel();
|
||||
_loadPending = true;
|
||||
}
|
||||
const auto finish = [=] {
|
||||
_saveRequestId = 0;
|
||||
if (base::take(_loadPending)) {
|
||||
_appConfigReloadForce = true;
|
||||
reload(true);
|
||||
} else {
|
||||
_appConfigReloadTimer.callOnce(kRefreshAppConfigTimeout);
|
||||
}
|
||||
};
|
||||
_saveRequestId = _api.request(MTPaccount_SetContentSettings(
|
||||
MTP_flags(enabled ? Flag::f_sensitive_enabled : Flag(0))
|
||||
)).done([=] {
|
||||
_requestId = 0;
|
||||
}).fail([=] {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
)).done(finish).fail(finish).send();
|
||||
_enabled = enabled;
|
||||
|
||||
_appConfigReloadTimer.callOnce(kRefreshAppConfigTimeout);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -22,7 +22,8 @@ class SensitiveContent final {
|
|||
public:
|
||||
explicit SensitiveContent(not_null<ApiWrap*> api);
|
||||
|
||||
void reload();
|
||||
void preload();
|
||||
void reload(bool force = false);
|
||||
void update(bool enabled);
|
||||
|
||||
[[nodiscard]] bool enabledCurrent() const;
|
||||
|
@ -32,10 +33,14 @@ public:
|
|||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
mtpRequestId _loadRequestId = 0;
|
||||
mtpRequestId _saveRequestId = 0;
|
||||
rpl::variable<bool> _enabled = false;
|
||||
rpl::variable<bool> _canChange = false;
|
||||
base::Timer _appConfigReloadTimer;
|
||||
bool _appConfigReloadForce = false;
|
||||
bool _loadPending = false;
|
||||
bool _loaded = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1074,7 +1074,7 @@ void Controller::fillSignaturesButton() {
|
|||
rpl::single(QString()),
|
||||
[] {},
|
||||
st::manageGroupTopButtonWithText,
|
||||
{ &st::menuIconSigned })));
|
||||
{ &st::menuIconProfile })));
|
||||
profiles->toggleOn(signs->toggledValue());
|
||||
profiles->finishAnimating();
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ PreviewWrap::PreviewWrap(
|
|||
}
|
||||
}, lifetime());
|
||||
session->data().itemViewRefreshRequest(
|
||||
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
if (item == _item) {
|
||||
if (goodItem()) {
|
||||
createView();
|
||||
|
|
|
@ -544,12 +544,9 @@ auto ChannelData::unavailableReasons() const
|
|||
return _unavailableReasons;
|
||||
}
|
||||
|
||||
void ChannelData::setUnavailableReasons(
|
||||
void ChannelData::setUnavailableReasonsList(
|
||||
std::vector<Data::UnavailableReason> &&reasons) {
|
||||
if (_unavailableReasons != reasons) {
|
||||
_unavailableReasons = std::move(reasons);
|
||||
session().changes().peerUpdated(this, UpdateFlag::UnavailableReason);
|
||||
}
|
||||
_unavailableReasons = std::move(reasons);
|
||||
}
|
||||
|
||||
void ChannelData::setAvailableMinId(MsgId availableMinId) {
|
||||
|
|
|
@ -433,9 +433,6 @@ public:
|
|||
return _ptsWaiter.waitingForShortPoll();
|
||||
}
|
||||
|
||||
void setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reason);
|
||||
|
||||
[[nodiscard]] MsgId availableMinId() const {
|
||||
return _availableMinId;
|
||||
}
|
||||
|
@ -515,6 +512,9 @@ private:
|
|||
-> const std::vector<Data::UnavailableReason> & override;
|
||||
bool canEditLastAdmin(not_null<UserData*> user) const;
|
||||
|
||||
void setUnavailableReasonsList(
|
||||
std::vector<Data::UnavailableReason> &&reasons) override;
|
||||
|
||||
Flags _flags = ChannelDataFlags(Flag::Forbidden);
|
||||
|
||||
PtsWaiter _ptsWaiter;
|
||||
|
|
|
@ -143,7 +143,7 @@ void DownloadManager::trackSession(not_null<Main::Session*> session) {
|
|||
}, data.lifetime);
|
||||
|
||||
session->data().itemViewRefreshRequest(
|
||||
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
changed(item);
|
||||
}, data.lifetime);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "data/data_peer.h"
|
||||
|
||||
#include "api/api_sensitive_content.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_chat_participant_status.h"
|
||||
|
@ -58,6 +59,11 @@ constexpr auto kUserpicSize = 160;
|
|||
|
||||
using UpdateFlag = Data::PeerUpdate::Flag;
|
||||
|
||||
[[nodiscard]] const std::vector<QString> &IgnoredReasons(
|
||||
not_null<Main::Session*> session) {
|
||||
return session->appConfig().ignoredRestrictionReasons();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Data {
|
||||
|
@ -85,10 +91,7 @@ UnavailableReason UnavailableReason::Sensitive() {
|
|||
QString UnavailableReason::Compute(
|
||||
not_null<Main::Session*> session,
|
||||
const std::vector<UnavailableReason> &list) {
|
||||
const auto &config = session->appConfig();
|
||||
const auto skip = config.get<std::vector<QString>>(
|
||||
"ignore_restriction_reasons",
|
||||
std::vector<QString>());
|
||||
const auto &skip = IgnoredReasons(session);
|
||||
auto &&filtered = ranges::views::all(
|
||||
list
|
||||
) | ranges::views::filter([&](const Data::UnavailableReason &reason) {
|
||||
|
@ -99,6 +102,13 @@ QString UnavailableReason::Compute(
|
|||
return (first != filtered.end()) ? first->text : QString();
|
||||
}
|
||||
|
||||
bool UnavailableReason::IgnoreSensitiveMark(
|
||||
not_null<Main::Session*> session) {
|
||||
return ranges::contains(
|
||||
IgnoredReasons(session),
|
||||
UnavailableReason::Sensitive().reason);
|
||||
}
|
||||
|
||||
// We should get a full restriction in "{full}: {reason}" format and we
|
||||
// need to find an "-all" tag in {full}, otherwise ignore this restriction.
|
||||
std::vector<UnavailableReason> UnavailableReason::Extract(
|
||||
|
@ -554,11 +564,45 @@ QString PeerData::computeUnavailableReason() const {
|
|||
unavailableReasons());
|
||||
}
|
||||
|
||||
bool PeerData::isUnavailableSensitive() const {
|
||||
return ranges::contains(
|
||||
unavailableReasons(),
|
||||
bool PeerData::hasSensitiveContent() const {
|
||||
return _sensitiveContent == 1;
|
||||
}
|
||||
|
||||
void PeerData::setUnavailableReasonsList(
|
||||
std::vector<Data::UnavailableReason> &&reasons) {
|
||||
Unexpected("PeerData::setUnavailableReasonsList.");
|
||||
}
|
||||
|
||||
void PeerData::setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reasons) {
|
||||
const auto i = ranges::find(
|
||||
reasons,
|
||||
true,
|
||||
&Data::UnavailableReason::sensitive);
|
||||
const auto sensitive = (i != end(reasons));
|
||||
if (sensitive) {
|
||||
reasons.erase(i);
|
||||
}
|
||||
auto changed = (sensitive != hasSensitiveContent());
|
||||
if (changed) {
|
||||
setHasSensitiveContent(sensitive);
|
||||
}
|
||||
if (reasons != unavailableReasons()) {
|
||||
setUnavailableReasonsList(std::move(reasons));
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
session().changes().peerUpdated(
|
||||
this,
|
||||
UpdateFlag::UnavailableReason);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerData::setHasSensitiveContent(bool has) {
|
||||
_sensitiveContent = has ? 1 : 0;
|
||||
if (has) {
|
||||
session().api().sensitiveContent().preload();
|
||||
}
|
||||
}
|
||||
|
||||
// This is duplicated in CanPinMessagesValue().
|
||||
|
|
|
@ -99,6 +99,8 @@ struct UnavailableReason {
|
|||
[[nodiscard]] static QString Compute(
|
||||
not_null<Main::Session*> session,
|
||||
const std::vector<UnavailableReason> &list);
|
||||
[[nodiscard]] static bool IgnoreSensitiveMark(
|
||||
not_null<Main::Session*> session);
|
||||
|
||||
[[nodiscard]] static std::vector<UnavailableReason> Extract(
|
||||
const MTPvector<MTPRestrictionReason> *list);
|
||||
|
@ -347,7 +349,9 @@ public:
|
|||
// If this string is not empty we must not allow to open the
|
||||
// conversation and we must show this string instead.
|
||||
[[nodiscard]] QString computeUnavailableReason() const;
|
||||
[[nodiscard]] bool isUnavailableSensitive() const;
|
||||
[[nodiscard]] bool hasSensitiveContent() const;
|
||||
void setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reason);
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr createOpenLink();
|
||||
[[nodiscard]] const ClickHandlerPtr &openLink() {
|
||||
|
@ -489,6 +493,10 @@ private:
|
|||
const ImageLocation &location,
|
||||
bool hasVideo);
|
||||
|
||||
virtual void setUnavailableReasonsList(
|
||||
std::vector<Data::UnavailableReason> &&reasons);
|
||||
void setHasSensitiveContent(bool has);
|
||||
|
||||
const not_null<Data::Session*> _owner;
|
||||
|
||||
mutable Data::CloudImage _userpic;
|
||||
|
@ -507,7 +515,8 @@ private:
|
|||
crl::time _lastFullUpdate = 0;
|
||||
|
||||
QString _name;
|
||||
uint32 _nameVersion : 31 = 1;
|
||||
uint32 _nameVersion : 30 = 1;
|
||||
uint32 _sensitiveContent : 1 = 0;
|
||||
uint32 _wallPaperOverriden : 1 = 0;
|
||||
|
||||
TimeId _ttlPeriod = 0;
|
||||
|
|
|
@ -252,8 +252,7 @@ Image *PhotoData::getReplyPreview(
|
|||
|
||||
Image *PhotoData::getReplyPreview(not_null<HistoryItem*> item) {
|
||||
const auto media = item->media();
|
||||
const auto spoiler = (media && media->hasSpoiler())
|
||||
|| item->hasSensitiveSpoiler();
|
||||
const auto spoiler = (media && media->hasSpoiler());
|
||||
return getReplyPreview(item->fullId(), item->history()->peer, spoiler);
|
||||
}
|
||||
|
||||
|
|
|
@ -311,6 +311,22 @@ Session::Session(not_null<Main::Session*> session)
|
|||
|
||||
_stories->loadMore(Data::StorySourcesList::NotHidden);
|
||||
});
|
||||
|
||||
session->appConfig().ignoredRestrictionReasonsChanges(
|
||||
) | rpl::start_with_next([=](std::vector<QString> &&changed) {
|
||||
auto refresh = std::vector<not_null<const HistoryItem*>>();
|
||||
for (const auto &[item, reasons] : _possiblyRestricted) {
|
||||
for (const auto &reason : changed) {
|
||||
if (reasons.contains(reason)) {
|
||||
refresh.push_back(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &item : refresh) {
|
||||
requestItemViewRefresh(item);
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Session::subscribeForTopicRepliesLists() {
|
||||
|
@ -1773,7 +1789,7 @@ rpl::producer<not_null<ViewElement*>> Session::viewResizeRequest() const {
|
|||
return _viewResizeRequest.events();
|
||||
}
|
||||
|
||||
void Session::requestItemViewRefresh(not_null<HistoryItem*> item) {
|
||||
void Session::requestItemViewRefresh(not_null<const HistoryItem*> item) {
|
||||
if (const auto view = item->mainView()) {
|
||||
notifyHistoryChangeDelayed(item->history());
|
||||
view->refreshInBlock();
|
||||
|
@ -1781,7 +1797,7 @@ void Session::requestItemViewRefresh(not_null<HistoryItem*> item) {
|
|||
_itemViewRefreshRequest.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<HistoryItem*>> Session::itemViewRefreshRequest() const {
|
||||
rpl::producer<not_null<const HistoryItem*>> Session::itemViewRefreshRequest() const {
|
||||
return _itemViewRefreshRequest.events();
|
||||
}
|
||||
|
||||
|
@ -1807,6 +1823,31 @@ void Session::requestItemTextRefresh(not_null<HistoryItem*> item) {
|
|||
}
|
||||
}
|
||||
|
||||
void Session::registerRestricted(
|
||||
not_null<const HistoryItem*> item,
|
||||
const QString &reason) {
|
||||
Expects(item->hasPossibleRestrictions());
|
||||
|
||||
_possiblyRestricted[item].emplace(reason);
|
||||
}
|
||||
|
||||
void Session::registerRestricted(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::vector<UnavailableReason> &reasons) {
|
||||
Expects(item->hasPossibleRestrictions());
|
||||
|
||||
auto &list = _possiblyRestricted[item];
|
||||
if (list.empty()) {
|
||||
auto &&simple = reasons
|
||||
| ranges::views::transform(&UnavailableReason::reason);
|
||||
list = { begin(simple), end(simple) };
|
||||
} else {
|
||||
for (const auto &reason : reasons) {
|
||||
list.emplace(reason.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::registerHighlightProcess(
|
||||
uint64 processId,
|
||||
not_null<HistoryItem*> item) {
|
||||
|
@ -2510,6 +2551,9 @@ void Session::unregisterMessage(not_null<HistoryItem*> item) {
|
|||
const auto peerId = item->history()->peer->id;
|
||||
const auto itemId = item->id;
|
||||
_itemRemoved.fire_copy(item);
|
||||
if (item->hasPossibleRestrictions()) {
|
||||
_possiblyRestricted.remove(item);
|
||||
}
|
||||
session().changes().messageUpdated(
|
||||
item,
|
||||
Data::MessageUpdate::Flag::Destroyed);
|
||||
|
|
|
@ -69,6 +69,7 @@ class SavedMessages;
|
|||
class Chatbots;
|
||||
class BusinessInfo;
|
||||
struct ReactionId;
|
||||
struct UnavailableReason;
|
||||
|
||||
struct RepliesReadTillUpdate {
|
||||
FullMsgId id;
|
||||
|
@ -288,8 +289,8 @@ public:
|
|||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemResizeRequest() const;
|
||||
void requestViewResize(not_null<ViewElement*> view);
|
||||
[[nodiscard]] rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
|
||||
void requestItemViewRefresh(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
|
||||
void requestItemViewRefresh(not_null<const HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemViewRefreshRequest() const;
|
||||
void requestItemTextRefresh(not_null<HistoryItem*> item);
|
||||
void requestUnreadReactionsAnimation(not_null<HistoryItem*> item);
|
||||
void notifyHistoryUnloaded(not_null<const History*> history);
|
||||
|
@ -313,6 +314,13 @@ public:
|
|||
void notifyPinnedDialogsOrderUpdated();
|
||||
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
|
||||
|
||||
void registerRestricted(
|
||||
not_null<const HistoryItem*> item,
|
||||
const QString &reason);
|
||||
void registerRestricted(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::vector<UnavailableReason> &reasons);
|
||||
|
||||
void registerHighlightProcess(
|
||||
uint64 processId,
|
||||
not_null<HistoryItem*> item);
|
||||
|
@ -920,7 +928,7 @@ private:
|
|||
rpl::event_stream<not_null<const ViewElement*>> _viewRepaintRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
|
||||
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemViewRefreshRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemViewRefreshRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemTextRefreshRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemDataChanges;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||
|
@ -1021,6 +1029,10 @@ private:
|
|||
base::flat_multi_map<TimeId, not_null<PollData*>> _pollsClosings;
|
||||
base::Timer _pollsClosingTimer;
|
||||
|
||||
base::flat_map<
|
||||
not_null<const HistoryItem*>,
|
||||
base::flat_set<QString>> _possiblyRestricted;
|
||||
|
||||
base::flat_map<FolderId, std::unique_ptr<Folder>> _folders;
|
||||
|
||||
std::unordered_map<
|
||||
|
|
|
@ -326,7 +326,7 @@ enum class MessageFlag : uint64 {
|
|||
EffectWatched = (1ULL << 46),
|
||||
|
||||
SensitiveContent = (1ULL << 47),
|
||||
AllowSensitive = (1ULL << 48),
|
||||
HasRestrictions = (1ULL << 48),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "data/data_user.h"
|
||||
|
||||
#include "api/api_sensitive_content.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_user_photos.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -117,14 +118,9 @@ auto UserData::unavailableReasons() const
|
|||
return _unavailableReasons;
|
||||
}
|
||||
|
||||
void UserData::setUnavailableReasons(
|
||||
void UserData::setUnavailableReasonsList(
|
||||
std::vector<Data::UnavailableReason> &&reasons) {
|
||||
if (_unavailableReasons != reasons) {
|
||||
_unavailableReasons = std::move(reasons);
|
||||
session().changes().peerUpdated(
|
||||
this,
|
||||
UpdateFlag::UnavailableReason);
|
||||
}
|
||||
_unavailableReasons = std::move(reasons);
|
||||
}
|
||||
|
||||
void UserData::setCommonChatsCount(int count) {
|
||||
|
@ -516,6 +512,10 @@ void UserData::setBirthday(Data::Birthday value) {
|
|||
if (_birthday != value) {
|
||||
_birthday = value;
|
||||
session().changes().peerUpdated(this, UpdateFlag::Birthday);
|
||||
|
||||
if (isSelf()) {
|
||||
session().api().sensitiveContent().reload(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,9 +185,6 @@ public:
|
|||
void setBirthday(Data::Birthday value);
|
||||
void setBirthday(const tl::conditional<MTPBirthday> &value);
|
||||
|
||||
void setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reasons);
|
||||
|
||||
int commonChatsCount() const;
|
||||
void setCommonChatsCount(int count);
|
||||
|
||||
|
@ -218,6 +215,9 @@ private:
|
|||
auto unavailableReasons() const
|
||||
-> const std::vector<Data::UnavailableReason> & override;
|
||||
|
||||
void setUnavailableReasonsList(
|
||||
std::vector<Data::UnavailableReason> &&reasons) override;
|
||||
|
||||
Flags _flags;
|
||||
Data::LastseenStatus _lastseen;
|
||||
Data::Birthday _birthday;
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "history/history_item.h"
|
||||
|
||||
#include "api/api_sensitive_content.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "calls/calls_instance.h" // Core::App().calls().joinGroupCall.
|
||||
|
@ -3356,6 +3357,8 @@ EffectId HistoryItem::effectId() const {
|
|||
|
||||
QString HistoryItem::computeUnavailableReason() const {
|
||||
if (const auto restrictions = Get<HistoryMessageRestrictions>()) {
|
||||
_flags |= MessageFlag::HasRestrictions;
|
||||
_history->owner().registerRestricted(this, restrictions->reasons);
|
||||
return Data::UnavailableReason::Compute(
|
||||
&history()->session(),
|
||||
restrictions->reasons);
|
||||
|
@ -3363,13 +3366,19 @@ QString HistoryItem::computeUnavailableReason() const {
|
|||
return QString();
|
||||
}
|
||||
|
||||
bool HistoryItem::hasSensitiveSpoiler() const {
|
||||
return (_flags & MessageFlag::SensitiveContent)
|
||||
&& !(_flags & MessageFlag::AllowSensitive);
|
||||
bool HistoryItem::isMediaSensitive() const {
|
||||
if (!(_flags & MessageFlag::SensitiveContent)
|
||||
&& !_history->peer->hasSensitiveContent()) {
|
||||
return false;
|
||||
}
|
||||
_flags |= MessageFlag::HasRestrictions;
|
||||
_history->owner().registerRestricted(this, u"sensitive"_q);
|
||||
return !Data::UnavailableReason::IgnoreSensitiveMark(
|
||||
&_history->session());
|
||||
}
|
||||
|
||||
void HistoryItem::allowSensitive() {
|
||||
_flags |= MessageFlag::AllowSensitive;
|
||||
bool HistoryItem::hasPossibleRestrictions() const {
|
||||
return _flags & MessageFlag::HasRestrictions;
|
||||
}
|
||||
|
||||
bool HistoryItem::isEmpty() const {
|
||||
|
@ -3666,10 +3675,10 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
|||
&Data::UnavailableReason::sensitive);
|
||||
if (i != end(restrictions->reasons)) {
|
||||
restrictions->reasons.erase(i);
|
||||
_flags |= MessageFlag::SensitiveContent;
|
||||
flagSensitiveContent();
|
||||
}
|
||||
} else if (!config.restrictions.empty()) {
|
||||
_flags |= MessageFlag::SensitiveContent;
|
||||
flagSensitiveContent();
|
||||
}
|
||||
|
||||
if (out() && isSending()) {
|
||||
|
@ -3679,6 +3688,11 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
|||
}
|
||||
}
|
||||
|
||||
void HistoryItem::flagSensitiveContent() {
|
||||
_flags |= MessageFlag::SensitiveContent;
|
||||
_history->session().api().sensitiveContent().preload();
|
||||
}
|
||||
|
||||
bool HistoryItem::checkRepliesPts(
|
||||
const HistoryMessageRepliesData &data) const {
|
||||
const auto channel = _history->peer->asChannel();
|
||||
|
|
|
@ -522,9 +522,9 @@ public:
|
|||
[[nodiscard]] bool isEmpty() const;
|
||||
[[nodiscard]] MessageGroupId groupId() const;
|
||||
[[nodiscard]] EffectId effectId() const;
|
||||
[[nodiscard]] bool hasPossibleRestrictions() const;
|
||||
[[nodiscard]] QString computeUnavailableReason() const;
|
||||
[[nodiscard]] bool hasSensitiveSpoiler() const;
|
||||
void allowSensitive();
|
||||
[[nodiscard]] bool isMediaSensitive() const;
|
||||
|
||||
[[nodiscard]] const HistoryMessageReplyMarkup *inlineReplyMarkup() const {
|
||||
return const_cast<HistoryItem*>(this)->inlineReplyMarkup();
|
||||
|
@ -660,6 +660,7 @@ private:
|
|||
[[nodiscard]] PreparedServiceText prepareCallScheduledText(
|
||||
TimeId scheduleDate);
|
||||
|
||||
void flagSensitiveContent();
|
||||
[[nodiscard]] PeerData *computeDisplayFrom() const;
|
||||
|
||||
const not_null<History*> _history;
|
||||
|
|
|
@ -736,6 +736,10 @@ void Element::refreshMedia(Element *replacing) {
|
|||
_flags &= ~Flag::HiddenByGroup;
|
||||
|
||||
const auto item = data();
|
||||
if (!item->computeUnavailableReason().isEmpty()) {
|
||||
_media = nullptr;
|
||||
return;
|
||||
}
|
||||
if (const auto media = item->media()) {
|
||||
if (media->canBeGrouped()) {
|
||||
if (const auto group = history()->owner().groups().find(item)) {
|
||||
|
@ -1004,7 +1008,12 @@ void Element::validateText() {
|
|||
: contextDependentText.links;
|
||||
setTextWithLinks(markedText, customLinks);
|
||||
} else {
|
||||
setTextWithLinks(_textItem->translatedTextWithLocalEntities());
|
||||
const auto unavailable = item->computeUnavailableReason();
|
||||
if (!unavailable.isEmpty()) {
|
||||
setTextWithLinks(Ui::Text::Italic(unavailable));
|
||||
} else {
|
||||
setTextWithLinks(_textItem->translatedTextWithLocalEntities());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -135,10 +135,13 @@ Gif::Gif(
|
|||
, _storyId(realParent->media()
|
||||
? realParent->media()->storyId()
|
||||
: FullStoryId())
|
||||
, _spoiler((spoiler || IsHiddenRoundMessage(_parent))
|
||||
, _spoiler((spoiler
|
||||
|| IsHiddenRoundMessage(_parent)
|
||||
|| realParent->isMediaSensitive())
|
||||
? std::make_unique<MediaSpoiler>()
|
||||
: nullptr)
|
||||
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
||||
, _downloadSize(Ui::FormatSizeText(_data->size))
|
||||
, _sensitiveSpoiler(realParent->isMediaSensitive()) {
|
||||
if (_data->isVideoMessage() && _parent->data()->media()->ttlSeconds()) {
|
||||
if (_spoiler) {
|
||||
_drawTtl = CreateTtlPaintCallback([=] { repaint(); });
|
||||
|
@ -582,9 +585,11 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
}
|
||||
|
||||
if (radial
|
||||
|| (!streamingMode
|
||||
&& ((!loaded && !_data->loading()) || !autoplay))) {
|
||||
const auto paintInCenter = !_sensitiveSpoiler
|
||||
&& (radial
|
||||
|| (!streamingMode
|
||||
&& ((!loaded && !_data->loading()) || !autoplay)));
|
||||
if (paintInCenter) {
|
||||
const auto radialRevealed = 1.;
|
||||
const auto opacity = (item->isSending() || _data->uploading())
|
||||
? 1.
|
||||
|
@ -652,6 +657,10 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
}
|
||||
p.setOpacity(1.);
|
||||
} else if (_sensitiveSpoiler) {
|
||||
drawSpoilerTag(p, rthumb, context, [&] {
|
||||
return spoilerTagBackground();
|
||||
});
|
||||
}
|
||||
if (displayMute) {
|
||||
auto muteRect = style::rtlrect(rthumb.x() + (rthumb.width() - st::historyVideoMessageMuteSize) / 2, rthumb.y() + st::msgDateImgDelta, st::historyVideoMessageMuteSize, st::historyVideoMessageMuteSize, width());
|
||||
|
@ -835,6 +844,28 @@ void Gif::paintTranscribe(
|
|||
context);
|
||||
}
|
||||
|
||||
void Gif::drawSpoilerTag(
|
||||
Painter &p,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const {
|
||||
Media::drawSpoilerTag(
|
||||
p,
|
||||
_spoiler.get(),
|
||||
_spoilerTag,
|
||||
rthumb,
|
||||
context,
|
||||
std::move(generateBackground));
|
||||
}
|
||||
|
||||
ClickHandlerPtr Gif::spoilerTagLink() const {
|
||||
return Media::spoilerTagLink(_spoiler.get(), _spoilerTag);
|
||||
}
|
||||
|
||||
QImage Gif::spoilerTagBackground() const {
|
||||
return _spoiler ? _spoiler->background : QImage();
|
||||
}
|
||||
|
||||
void Gif::validateVideoThumbnail() const {
|
||||
const auto content = _dataMedia->videoThumbnailContent();
|
||||
if (_videoThumbnailFrame || content.isEmpty()) {
|
||||
|
@ -1113,10 +1144,12 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
|
|||
}
|
||||
if (QRect(usex + paintx, painty, usew, painth).contains(point)) {
|
||||
ensureDataMediaCreated();
|
||||
result.link = (isRound && _parent->data()->media()->ttlSeconds())
|
||||
? _openl // Overriden.
|
||||
: (_spoiler && !_spoiler->revealed)
|
||||
? _spoiler->link
|
||||
result.link = (_spoiler && !_spoiler->revealed)
|
||||
? (_sensitiveSpoiler
|
||||
? spoilerTagLink()
|
||||
: (isRound && _parent->data()->media()->ttlSeconds())
|
||||
? _openl // Overriden.
|
||||
: _spoiler->link)
|
||||
: _data->uploading()
|
||||
? _cancell
|
||||
: _realParent->isSending()
|
||||
|
@ -1335,9 +1368,11 @@ void Gif::drawGrouped(
|
|||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
if (radial
|
||||
|| (!streamingMode
|
||||
&& ((!loaded && !_data->loading()) || !autoplay))) {
|
||||
const auto paintInCenter = !_sensitiveSpoiler
|
||||
&& (radial
|
||||
|| (!streamingMode
|
||||
&& ((!loaded && !_data->loading()) || !autoplay)));
|
||||
if (paintInCenter) {
|
||||
const auto radialRevealed = 1.;
|
||||
const auto opacity = (item->isSending() || _data->uploading())
|
||||
? 1.
|
||||
|
@ -1439,8 +1474,8 @@ TextState Gif::getStateGrouped(
|
|||
}
|
||||
}
|
||||
ensureDataMediaCreated();
|
||||
return TextState(_parent, (_spoiler && !_spoiler->revealed)
|
||||
? _spoiler->link
|
||||
auto link = (_spoiler && !_spoiler->revealed)
|
||||
? (_sensitiveSpoiler ? spoilerTagLink() : _spoiler->link)
|
||||
: _data->uploading()
|
||||
? _cancell
|
||||
: _realParent->isSending()
|
||||
|
@ -1449,7 +1484,8 @@ TextState Gif::getStateGrouped(
|
|||
? _openl
|
||||
: _data->loading()
|
||||
? _cancell
|
||||
: _savel);
|
||||
: _savel;
|
||||
return TextState(_parent, std::move(link));
|
||||
}
|
||||
|
||||
void Gif::ensureDataMediaCreated() const {
|
||||
|
|
|
@ -90,6 +90,14 @@ public:
|
|||
void stopAnimation() override;
|
||||
void checkAnimation() override;
|
||||
|
||||
void drawSpoilerTag(
|
||||
Painter &p,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const override;
|
||||
ClickHandlerPtr spoilerTagLink() const override;
|
||||
QImage spoilerTagBackground() const override;
|
||||
|
||||
void hideSpoilers() override;
|
||||
bool needsBubble() const override;
|
||||
bool unwrapped() const override;
|
||||
|
@ -203,6 +211,7 @@ private:
|
|||
const FullStoryId _storyId;
|
||||
std::unique_ptr<Streamed> _streamed;
|
||||
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||
mutable std::unique_ptr<MediaSpoilerTag> _spoilerTag;
|
||||
mutable std::unique_ptr<TranscribeButton> _transcribe;
|
||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
mutable std::unique_ptr<Image> _videoThumbnailFrame;
|
||||
|
@ -214,6 +223,7 @@ private:
|
|||
mutable bool _thumbIsEllipse : 1 = false;
|
||||
mutable bool _pollingStory : 1 = false;
|
||||
mutable bool _purchasedPriceTag : 1 = false;
|
||||
const bool _sensitiveSpoiler : 1 = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -13,13 +13,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_text_helper.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/media/history_view_media_common.h"
|
||||
#include "history/view/media/history_view_media_spoiler.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "lang/lang_tag.h" // FormatCountDecimal.
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
|
@ -31,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/text/text_utilities.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_menu_icons.h" // mediaMenuIconStealth.
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
@ -351,6 +354,156 @@ void Media::fillImageSpoiler(
|
|||
spoiler->cornerCache);
|
||||
}
|
||||
|
||||
void Media::drawSpoilerTag(
|
||||
Painter &p,
|
||||
not_null<MediaSpoiler*> spoiler,
|
||||
std::unique_ptr<MediaSpoilerTag> &tag,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const {
|
||||
if (!tag) {
|
||||
setupSpoilerTag(tag);
|
||||
if (!tag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto revealed = spoiler->revealAnimation.value(
|
||||
spoiler->revealed ? 1. : 0.);
|
||||
if (revealed == 1.) {
|
||||
return;
|
||||
}
|
||||
p.setOpacity(1. - revealed);
|
||||
const auto st = context.st;
|
||||
const auto darken = st->msgDateImgBg()->c;
|
||||
const auto fg = st->msgDateImgFg()->c;
|
||||
const auto star = st->creditsBg1()->c;
|
||||
if (tag->cache.isNull()
|
||||
|| tag->darken != darken
|
||||
|| tag->fg != fg
|
||||
|| tag->star != star) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto bg = generateBackground();
|
||||
if (bg.isNull()) {
|
||||
bg = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
|
||||
bg.fill(Qt::black);
|
||||
}
|
||||
|
||||
auto text = Ui::Text::String();
|
||||
auto iconSkip = 0;
|
||||
if (tag->sensitive) {
|
||||
text.setText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_sensitive_tag(tr::now));
|
||||
iconSkip = st::mediaMenuIconStealth.width() * 1.4;
|
||||
} else {
|
||||
const auto session = &history()->session();
|
||||
auto price = Ui::Text::Colorized(Ui::CreditsEmoji(session));
|
||||
price.append(Lang::FormatCountDecimal(tag->price));
|
||||
text.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_paid_price(
|
||||
tr::now,
|
||||
lt_price,
|
||||
price,
|
||||
Ui::Text::WithEntities),
|
||||
kMarkupTextOptions,
|
||||
Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = [] {},
|
||||
});
|
||||
}
|
||||
const auto width = iconSkip + text.maxWidth();
|
||||
const auto inner = QRect(0, 0, width, text.minHeight());
|
||||
const auto outer = inner.marginsAdded(st::paidTagPadding);
|
||||
const auto size = outer.size();
|
||||
const auto radius = std::min(size.width(), size.height()) / 2;
|
||||
auto cache = QImage(
|
||||
size * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
cache.setDevicePixelRatio(ratio);
|
||||
cache.fill(Qt::black);
|
||||
auto p = Painter(&cache);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawImage(
|
||||
QRect(
|
||||
(size.width() - rthumb.width()) / 2,
|
||||
(size.height() - rthumb.height()) / 2,
|
||||
rthumb.width(),
|
||||
rthumb.height()),
|
||||
bg);
|
||||
p.fillRect(QRect(QPoint(), size), darken);
|
||||
p.setPen(fg);
|
||||
p.setTextPalette(st->priceTagTextPalette());
|
||||
if (iconSkip) {
|
||||
st::mediaMenuIconStealth.paint(
|
||||
p,
|
||||
-outer.x(),
|
||||
(size.height() - st::mediaMenuIconStealth.height()) / 2,
|
||||
size.width(),
|
||||
fg);
|
||||
}
|
||||
text.draw(p, iconSkip - outer.x(), -outer.y(), width);
|
||||
p.end();
|
||||
|
||||
tag->darken = darken;
|
||||
tag->fg = fg;
|
||||
tag->cache = Images::Round(
|
||||
std::move(cache),
|
||||
Images::CornersMask(radius));
|
||||
}
|
||||
const auto &cache = tag->cache;
|
||||
const auto size = cache.size() / cache.devicePixelRatio();
|
||||
const auto left = rthumb.x() + (rthumb.width() - size.width()) / 2;
|
||||
const auto top = rthumb.y() + (rthumb.height() - size.height()) / 2;
|
||||
p.drawImage(left, top, cache);
|
||||
if (context.selected()) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto radius = std::min(size.width(), size.height()) / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st->msgSelectOverlay());
|
||||
p.drawRoundedRect(
|
||||
QRect(left, top, size.width(), size.height()),
|
||||
radius,
|
||||
radius);
|
||||
}
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
void Media::setupSpoilerTag(std::unique_ptr<MediaSpoilerTag> &tag) const {
|
||||
const auto item = parent()->data();
|
||||
if (item->isMediaSensitive()) {
|
||||
tag = std::make_unique<MediaSpoilerTag>();
|
||||
tag->sensitive = 1;
|
||||
return;
|
||||
}
|
||||
const auto media = parent()->data()->media();
|
||||
const auto invoice = media ? media->invoice() : nullptr;
|
||||
if (const auto price = invoice->isPaidMedia ? invoice->amount : 0) {
|
||||
tag = std::make_unique<MediaSpoilerTag>();
|
||||
tag->price = price;
|
||||
}
|
||||
}
|
||||
|
||||
ClickHandlerPtr Media::spoilerTagLink(
|
||||
not_null<MediaSpoiler*> spoiler,
|
||||
std::unique_ptr<MediaSpoilerTag> &tag) const {
|
||||
const auto item = parent()->data();
|
||||
if (!item->isRegular() || spoiler->revealed) {
|
||||
return nullptr;
|
||||
} else if (!tag) {
|
||||
setupSpoilerTag(tag);
|
||||
if (!tag) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (!tag->link) {
|
||||
tag->link = tag->sensitive
|
||||
? MakeSensitiveMediaLink(spoiler->link, item)
|
||||
: MakePaidMediaLink(item);
|
||||
}
|
||||
return tag->link;
|
||||
}
|
||||
|
||||
void Media::createSpoilerLink(not_null<MediaSpoiler*> spoiler) {
|
||||
const auto weak = base::make_weak(this);
|
||||
spoiler->link = std::make_shared<LambdaClickHandler>([weak, spoiler](
|
||||
|
|
|
@ -47,6 +47,7 @@ enum class InfoDisplayType : char;
|
|||
struct TextState;
|
||||
struct StateRequest;
|
||||
struct MediaSpoiler;
|
||||
struct MediaSpoilerTag;
|
||||
class StickerPlayer;
|
||||
class Element;
|
||||
struct SelectedQuote;
|
||||
|
@ -223,18 +224,18 @@ public:
|
|||
QPoint point,
|
||||
StateRequest request) const;
|
||||
|
||||
virtual void drawPriceTag(
|
||||
virtual void drawSpoilerTag(
|
||||
Painter &p,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const {
|
||||
Unexpected("Price tag method call.");
|
||||
Unexpected("Spoiler tag method call.");
|
||||
}
|
||||
[[nodiscard]] virtual ClickHandlerPtr priceTagLink() const {
|
||||
Unexpected("Price tag method call.");
|
||||
[[nodiscard]] virtual ClickHandlerPtr spoilerTagLink() const {
|
||||
Unexpected("Spoiler tag method call.");
|
||||
}
|
||||
[[nodiscard]] virtual QImage priceTagBackground() const {
|
||||
Unexpected("Price tag method call.");
|
||||
[[nodiscard]] virtual QImage spoilerTagBackground() const {
|
||||
Unexpected("Spoiler tag method call.");
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool animating() const {
|
||||
|
@ -390,6 +391,17 @@ protected:
|
|||
not_null<MediaSpoiler*> spoiler,
|
||||
QRect rect,
|
||||
const PaintContext &context) const;
|
||||
void drawSpoilerTag(
|
||||
Painter &p,
|
||||
not_null<MediaSpoiler*> spoiler,
|
||||
std::unique_ptr<MediaSpoilerTag> &tag,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const;
|
||||
void setupSpoilerTag(std::unique_ptr<MediaSpoilerTag> &tag) const;
|
||||
[[nodiscard]] ClickHandlerPtr spoilerTagLink(
|
||||
not_null<MediaSpoiler*> spoiler,
|
||||
std::unique_ptr<MediaSpoilerTag> &tag) const;
|
||||
void createSpoilerLink(not_null<MediaSpoiler*> spoiler);
|
||||
|
||||
void repaint() const;
|
||||
|
|
|
@ -7,10 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "history/view/media/history_view_media_common.h"
|
||||
|
||||
#include "api/api_sensitive_content.h"
|
||||
#include "api/api_views.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_document.h"
|
||||
|
@ -272,4 +278,62 @@ ClickHandlerPtr MakePaidMediaLink(not_null<HistoryItem*> item) {
|
|||
});
|
||||
}
|
||||
|
||||
ClickHandlerPtr MakeSensitiveMediaLink(
|
||||
ClickHandlerPtr reveal,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto session = &item->history()->session();
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
const auto show = controller ? controller->uiShow() : my.show;
|
||||
if (!show) {
|
||||
reveal->onClick(context);
|
||||
return;
|
||||
}
|
||||
show->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
struct State {
|
||||
rpl::variable<bool> canChange;
|
||||
Ui::Checkbox *checkbox = nullptr;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
const auto sensitive = &session->api().sensitiveContent();
|
||||
state->canChange = sensitive->canChange();
|
||||
const auto done = [=](Fn<void()> close) {
|
||||
if (state->canChange.current()
|
||||
&& state->checkbox->checked()) {
|
||||
show->showToast({
|
||||
.text = tr::lng_sensitive_toast(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue),
|
||||
.adaptive = true,
|
||||
.duration = 5 * crl::time(1000),
|
||||
});
|
||||
sensitive->update(true);
|
||||
} else {
|
||||
reveal->onClick(context);
|
||||
}
|
||||
close();
|
||||
};
|
||||
Ui::ConfirmBox(box, {
|
||||
.text = tr::lng_sensitive_text(Ui::Text::RichLangValue),
|
||||
.confirmed = done,
|
||||
.confirmText = tr::lng_sensitive_view(),
|
||||
.title = tr::lng_sensitive_title(),
|
||||
});
|
||||
const auto skip = st::defaultCheckbox.margin.bottom();
|
||||
const auto wrap = box->addRow(
|
||||
object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
|
||||
box,
|
||||
object_ptr<Ui::Checkbox>(
|
||||
box,
|
||||
tr::lng_sensitive_always(tr::now),
|
||||
false)),
|
||||
st::boxRowPadding + QMargins(0, 0, 0, skip));
|
||||
wrap->toggleOn(state->canChange.value());
|
||||
wrap->finishAnimating();
|
||||
state->checkbox = wrap->entity();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -75,6 +75,10 @@ void PaintInterpolatedIcon(
|
|||
int newWidth,
|
||||
int maxWidth);
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr MakePaidMediaLink(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] ClickHandlerPtr MakePaidMediaLink(
|
||||
not_null<HistoryItem*> item);
|
||||
[[nodiscard]] ClickHandlerPtr MakeSensitiveMediaLink(
|
||||
ClickHandlerPtr reveal,
|
||||
not_null<HistoryItem*> item);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -297,16 +297,19 @@ QMargins GroupedMedia::groupedPadding() const {
|
|||
(normal.bottom() - grouped.bottom()) + addToBottom);
|
||||
}
|
||||
|
||||
Media *GroupedMedia::lookupUnpaidMedia() const {
|
||||
Media *GroupedMedia::lookupSpoilerTagMedia() const {
|
||||
if (_parts.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto media = _parts.front().content.get();
|
||||
if (media && _parts.front().item->isMediaSensitive()) {
|
||||
return media;
|
||||
}
|
||||
const auto photo = media ? media->getPhoto() : nullptr;
|
||||
return (photo && photo->extendedMediaPreview()) ? media : nullptr;
|
||||
}
|
||||
|
||||
QImage GroupedMedia::generatePriceTagBackground(QRect full) const {
|
||||
QImage GroupedMedia::generateSpoilerTagBackground(QRect full) const {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto result = QImage(
|
||||
full.size() * ratio,
|
||||
|
@ -317,7 +320,7 @@ QImage GroupedMedia::generatePriceTagBackground(QRect full) const {
|
|||
const auto skip1 = st::historyGroupSkip / 2;
|
||||
const auto skip2 = st::historyGroupSkip - skip1;
|
||||
for (const auto &part : _parts) {
|
||||
auto background = part.content->priceTagBackground();
|
||||
auto background = part.content->spoilerTagBackground();
|
||||
const auto extended = part.geometry.translated(shift).marginsAdded(
|
||||
{ skip1, skip1, skip2, skip2 });
|
||||
if (background.isNull()) {
|
||||
|
@ -394,7 +397,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
|
|||
? Ui::BubbleRounding{ kSmall, kSmall, kSmall, kSmall }
|
||||
: adjustedBubbleRounding();
|
||||
auto highlight = context.highlight.range;
|
||||
const auto unpaid = lookupUnpaidMedia();
|
||||
const auto tagged = lookupSpoilerTagMedia();
|
||||
auto fullRect = QRect();
|
||||
const auto subpartHighlight = IsSubGroupSelection(highlight);
|
||||
for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
|
||||
|
@ -435,7 +438,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
|
|||
if (!part.cache.isNull()) {
|
||||
nowCache = true;
|
||||
}
|
||||
if (unpaid || _purchasedPriceTag) {
|
||||
if (tagged || _purchasedPriceTag) {
|
||||
fullRect = fullRect.united(part.geometry);
|
||||
}
|
||||
}
|
||||
|
@ -443,9 +446,9 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
|
|||
history()->owner().registerHeavyViewPart(_parent);
|
||||
}
|
||||
|
||||
if (unpaid) {
|
||||
unpaid->drawPriceTag(p, fullRect, context, [&] {
|
||||
return generatePriceTagBackground(fullRect);
|
||||
if (tagged) {
|
||||
tagged->drawSpoilerTag(p, fullRect, context, [&] {
|
||||
return generateSpoilerTagBackground(fullRect);
|
||||
});
|
||||
} else if (_purchasedPriceTag) {
|
||||
drawPurchasedTag(p, fullRect, context);
|
||||
|
@ -511,9 +514,11 @@ PointState GroupedMedia::pointState(QPoint point) const {
|
|||
TextState GroupedMedia::textState(QPoint point, StateRequest request) const {
|
||||
const auto groupPadding = groupedPadding();
|
||||
auto result = getPartState(point - QPoint(0, groupPadding.top()), request);
|
||||
if (const auto unpaid = lookupUnpaidMedia()) {
|
||||
if (const auto tagged = lookupSpoilerTagMedia()) {
|
||||
if (QRect(0, 0, width(), height()).contains(point)) {
|
||||
result.link = unpaid->priceTagLink();
|
||||
if (auto link = tagged->spoilerTagLink()) {
|
||||
result.link = std::move(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_parent->media() == this && (!_parent->hasBubble() || isBubbleBottom())) {
|
||||
|
|
|
@ -149,8 +149,8 @@ private:
|
|||
RectParts sides) const;
|
||||
[[nodiscard]] QMargins groupedPadding() const;
|
||||
|
||||
[[nodiscard]] Media *lookupUnpaidMedia() const;
|
||||
[[nodiscard]] QImage generatePriceTagBackground(QRect full) const;
|
||||
[[nodiscard]] Media *lookupSpoilerTagMedia() const;
|
||||
[[nodiscard]] QImage generateSpoilerTagBackground(QRect full) const;
|
||||
|
||||
mutable std::optional<HistoryItem*> _captionItem;
|
||||
std::vector<Part> _parts;
|
||||
|
|
|
@ -26,4 +26,14 @@ struct MediaSpoiler {
|
|||
bool revealed = false;
|
||||
};
|
||||
|
||||
struct MediaSpoilerTag {
|
||||
uint64 price : 63 = 0;
|
||||
uint64 sensitive : 1 = 0;
|
||||
QImage cache;
|
||||
QColor darken;
|
||||
QColor fg;
|
||||
QColor star;
|
||||
ClickHandlerPtr link;
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -63,15 +63,6 @@ struct Photo::Streamed {
|
|||
QImage roundingMask;
|
||||
};
|
||||
|
||||
struct Photo::PriceTag {
|
||||
uint64 price = 0;
|
||||
QImage cache;
|
||||
QColor darken;
|
||||
QColor fg;
|
||||
QColor star;
|
||||
ClickHandlerPtr link;
|
||||
};
|
||||
|
||||
Photo::Streamed::Streamed(
|
||||
std::shared_ptr<::Media::Streaming::Document> shared)
|
||||
: instance(std::move(shared), nullptr) {
|
||||
|
@ -87,7 +78,10 @@ Photo::Photo(
|
|||
, _storyId(realParent->media()
|
||||
? realParent->media()->storyId()
|
||||
: FullStoryId())
|
||||
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
|
||||
, _spoiler((spoiler || realParent->isMediaSensitive())
|
||||
? std::make_unique<MediaSpoiler>()
|
||||
: nullptr)
|
||||
, _sensitiveSpoiler(realParent->isMediaSensitive() ? 1 : 0) {
|
||||
create(realParent->fullId());
|
||||
}
|
||||
|
||||
|
@ -342,7 +336,8 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
|
||||
const auto showEnlarge = loaded && _showEnlarge;
|
||||
const auto paintInCenter = (radial || (!loaded && !_data->loading()));
|
||||
const auto paintInCenter = !_sensitiveSpoiler
|
||||
&& (radial || (!loaded && !_data->loading()));
|
||||
if (paintInCenter || showEnlarge) {
|
||||
p.setPen(Qt::NoPen);
|
||||
if (context.selected()) {
|
||||
|
@ -382,9 +377,9 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, sti->historyFileThumbRadialFg);
|
||||
}
|
||||
} else if (preview) {
|
||||
drawPriceTag(p, rthumb, context, [&] {
|
||||
return priceTagBackground();
|
||||
} else if (_sensitiveSpoiler || preview) {
|
||||
drawSpoilerTag(p, rthumb, context, [&] {
|
||||
return spoilerTagBackground();
|
||||
});
|
||||
}
|
||||
if (showEnlarge) {
|
||||
|
@ -426,105 +421,18 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Photo::setupPriceTag() const {
|
||||
const auto media = parent()->data()->media();
|
||||
const auto invoice = media ? media->invoice() : nullptr;
|
||||
const auto price = invoice->isPaidMedia ? invoice->amount : 0;
|
||||
if (!price) {
|
||||
return;
|
||||
}
|
||||
_priceTag = std::make_unique<PriceTag>();
|
||||
_priceTag->price = price;
|
||||
}
|
||||
|
||||
void Photo::drawPriceTag(
|
||||
void Photo::drawSpoilerTag(
|
||||
Painter &p,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const {
|
||||
if (!_priceTag) {
|
||||
setupPriceTag();
|
||||
if (!_priceTag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto st = context.st;
|
||||
const auto darken = st->msgDateImgBg()->c;
|
||||
const auto fg = st->msgDateImgFg()->c;
|
||||
const auto star = st->creditsBg1()->c;
|
||||
if (_priceTag->cache.isNull()
|
||||
|| _priceTag->darken != darken
|
||||
|| _priceTag->fg != fg
|
||||
|| _priceTag->star != star) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto bg = generateBackground();
|
||||
if (bg.isNull()) {
|
||||
bg = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
|
||||
bg.fill(Qt::black);
|
||||
}
|
||||
|
||||
auto text = Ui::Text::String();
|
||||
const auto session = &history()->session();
|
||||
auto price = Ui::Text::Colorized(Ui::CreditsEmoji(session));
|
||||
price.append(Lang::FormatCountDecimal(_priceTag->price));
|
||||
text.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_paid_price(
|
||||
tr::now,
|
||||
lt_price,
|
||||
price,
|
||||
Ui::Text::WithEntities),
|
||||
kMarkupTextOptions,
|
||||
Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = [] {},
|
||||
});
|
||||
const auto width = text.maxWidth();
|
||||
const auto inner = QRect(0, 0, width, text.minHeight());
|
||||
const auto outer = inner.marginsAdded(st::paidTagPadding);
|
||||
const auto size = outer.size();
|
||||
const auto radius = std::min(size.width(), size.height()) / 2;
|
||||
auto cache = QImage(
|
||||
size * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
cache.setDevicePixelRatio(ratio);
|
||||
cache.fill(Qt::black);
|
||||
auto p = Painter(&cache);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawImage(
|
||||
QRect(
|
||||
(size.width() - rthumb.width()) / 2,
|
||||
(size.height() - rthumb.height()) / 2,
|
||||
rthumb.width(),
|
||||
rthumb.height()),
|
||||
bg);
|
||||
p.fillRect(QRect(QPoint(), size), darken);
|
||||
p.setPen(fg);
|
||||
p.setTextPalette(st->priceTagTextPalette());
|
||||
text.draw(p, -outer.x(), -outer.y(), width);
|
||||
p.end();
|
||||
|
||||
_priceTag->darken = darken;
|
||||
_priceTag->fg = fg;
|
||||
_priceTag->cache = Images::Round(
|
||||
std::move(cache),
|
||||
Images::CornersMask(radius));
|
||||
}
|
||||
const auto &cache = _priceTag->cache;
|
||||
const auto size = cache.size() / cache.devicePixelRatio();
|
||||
const auto left = rthumb.x() + (rthumb.width() - size.width()) / 2;
|
||||
const auto top = rthumb.y() + (rthumb.height() - size.height()) / 2;
|
||||
p.drawImage(left, top, cache);
|
||||
if (context.selected()) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto radius = std::min(size.width(), size.height()) / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st->msgSelectOverlay());
|
||||
p.drawRoundedRect(
|
||||
QRect(left, top, size.width(), size.height()),
|
||||
radius,
|
||||
radius);
|
||||
}
|
||||
Media::drawSpoilerTag(
|
||||
p,
|
||||
_spoiler.get(),
|
||||
_spoilerTag,
|
||||
rthumb,
|
||||
context,
|
||||
std::move(generateBackground));
|
||||
}
|
||||
|
||||
void Photo::validateUserpicImageCache(QSize size, bool forum) const {
|
||||
|
@ -734,23 +642,11 @@ QRect Photo::enlargeRect() const {
|
|||
};
|
||||
}
|
||||
|
||||
ClickHandlerPtr Photo::priceTagLink() const {
|
||||
const auto item = parent()->data();
|
||||
if (!item->isRegular()) {
|
||||
return nullptr;
|
||||
} else if (!_priceTag) {
|
||||
setupPriceTag();
|
||||
if (!_priceTag) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (!_priceTag->link) {
|
||||
_priceTag->link = MakePaidMediaLink(item);
|
||||
}
|
||||
return _priceTag->link;
|
||||
ClickHandlerPtr Photo::spoilerTagLink() const {
|
||||
return Media::spoilerTagLink(_spoiler.get(), _spoilerTag);
|
||||
}
|
||||
|
||||
QImage Photo::priceTagBackground() const {
|
||||
QImage Photo::spoilerTagBackground() const {
|
||||
return _spoiler ? _spoiler->background : QImage();
|
||||
}
|
||||
|
||||
|
@ -767,10 +663,10 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
|
|||
|
||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||
ensureDataMediaCreated();
|
||||
result.link = _data->extendedMediaPreview()
|
||||
? priceTagLink()
|
||||
: (_spoiler && !_spoiler->revealed)
|
||||
? _spoiler->link
|
||||
result.link = (_spoiler && !_spoiler->revealed)
|
||||
? ((_data->extendedMediaPreview() || _sensitiveSpoiler)
|
||||
? spoilerTagLink()
|
||||
: _spoiler->link)
|
||||
: _data->uploading()
|
||||
? _cancell
|
||||
: _dataMedia->loaded()
|
||||
|
@ -875,10 +771,11 @@ void Photo::drawGrouped(
|
|||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
const auto displayState = radial
|
||||
|| (!loaded && !_data->loading())
|
||||
|| _data->waitingForAlbum();
|
||||
if (displayState) {
|
||||
const auto paintInCenter = !_sensitiveSpoiler
|
||||
&& (radial
|
||||
|| (!loaded && !_data->loading())
|
||||
|| _data->waitingForAlbum());
|
||||
if (paintInCenter) {
|
||||
const auto radialOpacity = radial
|
||||
? _animation->radial.opacity()
|
||||
: 1.;
|
||||
|
@ -941,17 +838,18 @@ TextState Photo::getStateGrouped(
|
|||
return {};
|
||||
}
|
||||
ensureDataMediaCreated();
|
||||
return TextState(_parent, _data->extendedMediaPreview()
|
||||
? priceTagLink()
|
||||
: (_spoiler && !_spoiler->revealed)
|
||||
? _spoiler->link
|
||||
auto link = (_spoiler && !_spoiler->revealed)
|
||||
? ((_data->extendedMediaPreview() || _sensitiveSpoiler)
|
||||
? spoilerTagLink()
|
||||
: _spoiler->link)
|
||||
: _data->uploading()
|
||||
? _cancell
|
||||
: _dataMedia->loaded()
|
||||
? _openl
|
||||
: _data->loading()
|
||||
? _cancell
|
||||
: _savel);
|
||||
: _savel;
|
||||
return TextState(_parent, std::move(link));
|
||||
}
|
||||
|
||||
float64 Photo::dataProgress() const {
|
||||
|
|
|
@ -75,13 +75,13 @@ public:
|
|||
QPoint point,
|
||||
StateRequest request) const override;
|
||||
|
||||
void drawPriceTag(
|
||||
void drawSpoilerTag(
|
||||
Painter &p,
|
||||
QRect rthumb,
|
||||
const PaintContext &context,
|
||||
Fn<QImage()> generateBackground) const override;
|
||||
ClickHandlerPtr priceTagLink() const override;
|
||||
QImage priceTagBackground() const override;
|
||||
ClickHandlerPtr spoilerTagLink() const override;
|
||||
QImage spoilerTagBackground() const override;
|
||||
|
||||
void hideSpoilers() override;
|
||||
bool needsBubble() const override;
|
||||
|
@ -105,7 +105,6 @@ protected:
|
|||
|
||||
private:
|
||||
struct Streamed;
|
||||
struct PriceTag;
|
||||
|
||||
void create(FullMsgId contextId, PeerData *chat = nullptr);
|
||||
|
||||
|
@ -115,7 +114,7 @@ private:
|
|||
|
||||
void ensureDataMediaCreated() const;
|
||||
void dataMediaCreated() const;
|
||||
void setupPriceTag() const;
|
||||
void setupSpoilerTag() const;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
@ -161,14 +160,15 @@ private:
|
|||
|
||||
const not_null<PhotoData*> _data;
|
||||
const FullStoryId _storyId;
|
||||
mutable std::unique_ptr<PriceTag> _priceTag;
|
||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||
mutable std::unique_ptr<Streamed> _streamed;
|
||||
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||
mutable std::unique_ptr<MediaSpoilerTag> _spoilerTag;
|
||||
mutable QImage _imageCache;
|
||||
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
||||
uint32 _serviceWidth : 27 = 0;
|
||||
uint32 _serviceWidth : 26 = 0;
|
||||
uint32 _purchasedPriceTag : 1 = 0;
|
||||
const uint32 _sensitiveSpoiler : 1 = 0;
|
||||
mutable uint32 _imageCacheForum : 1 = 0;
|
||||
mutable uint32 _imageCacheBlurred : 1 = 0;
|
||||
mutable uint32 _pollingStory : 1 = 0;
|
||||
|
|
|
@ -38,15 +38,18 @@ void AppConfig::start() {
|
|||
}, _lifetime);
|
||||
}
|
||||
|
||||
void AppConfig::refresh() {
|
||||
void AppConfig::refresh(bool force) {
|
||||
if (_requestId || !_api) {
|
||||
if (force) {
|
||||
_pendingRefresh = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_pendingRefresh = false;
|
||||
_requestId = _api->request(MTPhelp_GetAppConfig(
|
||||
MTP_int(_hash)
|
||||
)).done([=](const MTPhelp_AppConfig &result) {
|
||||
_requestId = 0;
|
||||
refreshDelayed();
|
||||
result.match([&](const MTPDhelp_appConfig &data) {
|
||||
_hash = data.vhash().v;
|
||||
|
||||
|
@ -55,15 +58,25 @@ void AppConfig::refresh() {
|
|||
LOG(("API Error: Unexpected config type."));
|
||||
return;
|
||||
}
|
||||
auto was = ignoredRestrictionReasons();
|
||||
|
||||
_data.clear();
|
||||
for (const auto &element : config.c_jsonObject().vvalue().v) {
|
||||
element.match([&](const MTPDjsonObjectValue &data) {
|
||||
_data.emplace_or_assign(qs(data.vkey()), data.vvalue());
|
||||
});
|
||||
}
|
||||
updateIgnoredRestrictionReasons(std::move(was));
|
||||
|
||||
DEBUG_LOG(("getAppConfig result handled."));
|
||||
_refreshed.fire({});
|
||||
}, [](const MTPDhelp_appConfigNotModified &) {});
|
||||
|
||||
if (base::take(_pendingRefresh)) {
|
||||
refresh();
|
||||
} else {
|
||||
refreshDelayed();
|
||||
}
|
||||
}).fail([=] {
|
||||
_requestId = 0;
|
||||
refreshDelayed();
|
||||
|
@ -76,6 +89,24 @@ void AppConfig::refreshDelayed() {
|
|||
});
|
||||
}
|
||||
|
||||
void AppConfig::updateIgnoredRestrictionReasons(std::vector<QString> was) {
|
||||
_ignoreRestrictionReasons = get<std::vector<QString>>(
|
||||
u"ignore_restriction_reasons"_q,
|
||||
std::vector<QString>());
|
||||
ranges::sort(_ignoreRestrictionReasons);
|
||||
if (_ignoreRestrictionReasons != was) {
|
||||
for (const auto &reason : _ignoreRestrictionReasons) {
|
||||
const auto i = ranges::remove(was, reason);
|
||||
if (i != end(was)) {
|
||||
was.erase(i, end(was));
|
||||
} else {
|
||||
was.push_back(reason);
|
||||
}
|
||||
}
|
||||
_ignoreRestrictionChanges.fire(std::move(was));
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> AppConfig::refreshed() const {
|
||||
return _refreshed.events();
|
||||
}
|
||||
|
|
|
@ -55,7 +55,15 @@ public:
|
|||
|
||||
[[nodiscard]] bool newRequirePremiumFree() const;
|
||||
|
||||
void refresh();
|
||||
[[nodiscard]] auto ignoredRestrictionReasons() const
|
||||
-> const std::vector<QString> & {
|
||||
return _ignoreRestrictionReasons;
|
||||
}
|
||||
[[nodiscard]] auto ignoredRestrictionReasonsChanges() const {
|
||||
return _ignoreRestrictionChanges.events();
|
||||
}
|
||||
|
||||
void refresh(bool force = false);
|
||||
|
||||
private:
|
||||
void refreshDelayed();
|
||||
|
@ -84,14 +92,20 @@ private:
|
|||
const QString &key,
|
||||
std::vector<int> &&fallback) const;
|
||||
|
||||
void updateIgnoredRestrictionReasons(std::vector<QString> was);
|
||||
|
||||
const not_null<Account*> _account;
|
||||
std::optional<MTP::Sender> _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
int32 _hash = 0;
|
||||
bool _pendingRefresh = false;
|
||||
base::flat_map<QString, MTPJSONValue> _data;
|
||||
rpl::event_stream<> _refreshed;
|
||||
base::flat_set<QString> _dismissedSuggestions;
|
||||
|
||||
std::vector<QString> _ignoreRestrictionReasons;
|
||||
rpl::event_stream<std::vector<QString>> _ignoreRestrictionChanges;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "settings/settings_chat.h"
|
||||
|
||||
#include "base/timer_rpl.h"
|
||||
#include "settings/settings_advanced.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "settings/settings_experimental.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "boxes/peers/edit_peer_color_box.h"
|
||||
|
@ -1022,7 +1024,6 @@ void SetupMessages(
|
|||
void SetupArchive(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::VerticalLayout*> container) {
|
||||
Ui::AddDivider(container);
|
||||
Ui::AddSkip(container);
|
||||
|
||||
PreloadArchiveSettings(&controller->session());
|
||||
|
@ -1801,12 +1802,17 @@ void Chat::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
|
|||
void Chat::setupContent(not_null<Window::SessionController*> controller) {
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
|
||||
auto updateOnTick = rpl::single(
|
||||
) | rpl::then(base::timer_each(60 * crl::time(1000)));
|
||||
|
||||
SetupThemeOptions(controller, content);
|
||||
SetupThemeSettings(controller, content);
|
||||
SetupCloudThemes(controller, content);
|
||||
SetupChatBackground(controller, content);
|
||||
SetupStickersEmoji(controller, content);
|
||||
SetupMessages(controller, content);
|
||||
Ui::AddDivider(content);
|
||||
SetupSensitiveContent(controller, content, std::move(updateOnTick));
|
||||
SetupArchive(controller, content);
|
||||
|
||||
Ui::ResizeFitChild(this, content);
|
||||
|
|
|
@ -577,47 +577,6 @@ void SetupTopPeers(
|
|||
Ui::AddDividerText(container, tr::lng_settings_top_peers_about());
|
||||
}
|
||||
|
||||
void SetupSensitiveContent(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<> updateTrigger) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto inner = wrap->entity();
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSubsectionTitle(inner, tr::lng_settings_sensitive_title());
|
||||
|
||||
const auto session = &controller->session();
|
||||
|
||||
std::move(
|
||||
updateTrigger
|
||||
) | rpl::start_with_next([=] {
|
||||
session->api().sensitiveContent().reload();
|
||||
}, container->lifetime());
|
||||
inner->add(object_ptr<Button>(
|
||||
inner,
|
||||
tr::lng_settings_sensitive_disable_filtering(),
|
||||
st::settingsButtonNoIcon
|
||||
))->toggleOn(
|
||||
session->api().sensitiveContent().enabled()
|
||||
)->toggledChanges(
|
||||
) | rpl::filter([=](bool toggled) {
|
||||
return toggled != session->api().sensitiveContent().enabledCurrent();
|
||||
}) | rpl::start_with_next([=](bool toggled) {
|
||||
session->api().sensitiveContent().update(toggled);
|
||||
}, container->lifetime());
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddDividerText(inner, tr::lng_settings_sensitive_about());
|
||||
|
||||
wrap->toggleOn(session->api().sensitiveContent().canChange());
|
||||
}
|
||||
|
||||
void SetupSelfDestruction(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
|
@ -911,6 +870,47 @@ void SetupSecurity(
|
|||
|
||||
} // namespace
|
||||
|
||||
void SetupSensitiveContent(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<> updateTrigger) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto inner = wrap->entity();
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSubsectionTitle(inner, tr::lng_settings_sensitive_title());
|
||||
|
||||
const auto session = &controller->session();
|
||||
|
||||
std::move(
|
||||
updateTrigger
|
||||
) | rpl::start_with_next([=] {
|
||||
session->api().sensitiveContent().reload();
|
||||
}, container->lifetime());
|
||||
inner->add(object_ptr<Button>(
|
||||
inner,
|
||||
tr::lng_settings_sensitive_disable_filtering(),
|
||||
st::settingsButtonNoIcon
|
||||
))->toggleOn(
|
||||
session->api().sensitiveContent().enabled()
|
||||
)->toggledChanges(
|
||||
) | rpl::filter([=](bool toggled) {
|
||||
return toggled != session->api().sensitiveContent().enabledCurrent();
|
||||
}) | rpl::start_with_next([=](bool toggled) {
|
||||
session->api().sensitiveContent().update(toggled);
|
||||
}, container->lifetime());
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddDividerText(inner, tr::lng_settings_sensitive_about());
|
||||
|
||||
wrap->toggleOn(session->api().sensitiveContent().canChange());
|
||||
}
|
||||
|
||||
int ExceptionUsersCount(const std::vector<not_null<PeerData*>> &exceptions) {
|
||||
const auto add = [](int already, not_null<PeerData*> peer) {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
|
@ -1099,11 +1099,6 @@ void PrivacySecurity::setupContent(
|
|||
SetupSecurity(controller, content, trigger(), showOtherMethod());
|
||||
SetupPrivacy(controller, content, trigger());
|
||||
SetupTopPeers(controller, content);
|
||||
#if !defined OS_MAC_STORE && !defined OS_WIN_STORE
|
||||
SetupSensitiveContent(controller, content, trigger());
|
||||
#else // !OS_MAC_STORE && !OS_WIN_STORE
|
||||
AddDivider(content);
|
||||
#endif // !OS_MAC_STORE && !OS_WIN_STORE
|
||||
SetupArchiveAndMute(controller, content);
|
||||
SetupConfirmationExtensions(controller, content);
|
||||
SetupBotsAndWebsites(controller, content);
|
||||
|
|
|
@ -18,6 +18,11 @@ class BoxContent;
|
|||
|
||||
namespace Settings {
|
||||
|
||||
void SetupSensitiveContent(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<> updateTrigger);
|
||||
|
||||
int ExceptionUsersCount(const std::vector<not_null<PeerData*>> &exceptions);
|
||||
|
||||
bool CheckEditCloudPassword(not_null<::Main::Session*> session);
|
||||
|
|
Loading…
Add table
Reference in a new issue