mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Proof-of-concept read time in private chats.
This commit is contained in:
parent
e63d573414
commit
474f1118b6
4 changed files with 128 additions and 20 deletions
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "api/api_who_reacted.h"
|
||||
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
|
@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_message_reaction_id.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -36,10 +38,11 @@ namespace {
|
|||
constexpr auto kContextReactionsLimit = 50;
|
||||
|
||||
using Data::ReactionId;
|
||||
using WhoReadState = Ui::WhoReadState;
|
||||
|
||||
struct Peers {
|
||||
std::vector<WhoReadPeer> list;
|
||||
bool unknown = false;
|
||||
WhoReadState state = WhoReadState::Empty;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Peers &a,
|
||||
|
@ -59,7 +62,7 @@ struct PeersWithReactions {
|
|||
std::vector<PeerWithReaction> list;
|
||||
std::vector<WhoReadPeer> read;
|
||||
int fullReactionsCount = 0;
|
||||
bool unknown = false;
|
||||
WhoReadState state = WhoReadState::Empty;
|
||||
|
||||
friend inline bool operator==(
|
||||
const PeersWithReactions &a,
|
||||
|
@ -68,7 +71,7 @@ struct PeersWithReactions {
|
|||
|
||||
struct CachedRead {
|
||||
CachedRead()
|
||||
: data(Peers{ .unknown = true }) {
|
||||
: data(Peers{ .state = WhoReadState::Unknown }) {
|
||||
}
|
||||
rpl::variable<Peers> data;
|
||||
mtpRequestId requestId = 0;
|
||||
|
@ -76,7 +79,7 @@ struct CachedRead {
|
|||
|
||||
struct CachedReacted {
|
||||
CachedReacted()
|
||||
: data(PeersWithReactions{ .unknown = true }) {
|
||||
: data(PeersWithReactions{ .state = WhoReadState::Unknown }) {
|
||||
}
|
||||
rpl::variable<PeersWithReactions> data;
|
||||
mtpRequestId requestId = 0;
|
||||
|
@ -186,6 +189,27 @@ struct State {
|
|||
context->cachedReacted.erase(j);
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
Data::AmPremiumValue(
|
||||
session
|
||||
) | rpl::skip(1) | rpl::filter(
|
||||
rpl::mappers::_1
|
||||
) | rpl::start_with_next([=] {
|
||||
for (auto &[item, cache] : context->cachedRead) {
|
||||
if (cache.data.current().state == Ui::WhoReadState::MyHidden) {
|
||||
cache.data = Peers{ .state = Ui::WhoReadState::Unknown };
|
||||
}
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
session->api().globalPrivacy().hideReadTime(
|
||||
) | rpl::skip(1) | rpl::filter(
|
||||
!rpl::mappers::_1
|
||||
) | rpl::start_with_next([=] {
|
||||
for (auto &[item, cache] : context->cachedRead) {
|
||||
if (cache.data.current().state == Ui::WhoReadState::MyHidden) {
|
||||
cache.data = Peers{ .state = Ui::WhoReadState::Unknown };
|
||||
}
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -222,7 +246,38 @@ struct State {
|
|||
}
|
||||
const auto context = PreparedContextAt(weak.data(), session);
|
||||
auto &entry = context->cacheRead(item);
|
||||
if (!entry.requestId) {
|
||||
if (entry.requestId) {
|
||||
} else if (const auto user = item->history()->peer->asUser()) {
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetOutboxReadDate(
|
||||
user->input,
|
||||
MTP_int(item->id)
|
||||
)
|
||||
).done([=](const MTPOutboxReadDate &result) {
|
||||
const auto &data = result.data();
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
auto parsed = Peers();
|
||||
parsed.list.push_back({
|
||||
.peer = user->id,
|
||||
.date = data.vdate().v,
|
||||
});
|
||||
entry.data = std::move(parsed);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
if (entry.data.current().state == WhoReadState::Unknown) {
|
||||
const auto &text = error.type();
|
||||
entry.data = (text == u"YOUR_PRIVACY_RESTRICTED"_q)
|
||||
? Peers{ .state = WhoReadState::MyHidden }
|
||||
: (text == u"USER_PRIVACY_RESTRICTED"_q)
|
||||
? Peers{ .state = WhoReadState::HisHidden }
|
||||
: (text == u"MESSAGE_TOO_OLD"_q)
|
||||
? Peers{ .state = WhoReadState::TooOld }
|
||||
: Peers{ .state = WhoReadState::Empty };
|
||||
}
|
||||
}).send();
|
||||
} else {
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetMessageReadParticipants(
|
||||
item->history()->peer->input,
|
||||
|
@ -243,8 +298,8 @@ struct State {
|
|||
}).fail([=] {
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
if (entry.data.current().unknown) {
|
||||
entry.data = Peers();
|
||||
if (entry.data.current().state == WhoReadState::Unknown) {
|
||||
entry.data = Peers{ .state = WhoReadState::Empty };
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
@ -258,7 +313,7 @@ struct State {
|
|||
.list = peers.list | ranges::views::transform([](WhoReadPeer peer) {
|
||||
return PeerWithReaction{ .peerWithDate = peer };
|
||||
}) | ranges::to_vector,
|
||||
.unknown = peers.unknown,
|
||||
.state = peers.state,
|
||||
};
|
||||
result.read = std::move(peers.list);
|
||||
return result;
|
||||
|
@ -319,8 +374,10 @@ struct State {
|
|||
}).fail([=] {
|
||||
auto &entry = context->cacheReacted(item, reaction);
|
||||
entry.requestId = 0;
|
||||
if (entry.data.current().unknown) {
|
||||
entry.data = PeersWithReactions();
|
||||
if (entry.data.current().state == WhoReadState::Unknown) {
|
||||
entry.data = PeersWithReactions{
|
||||
.state = WhoReadState::Empty,
|
||||
};
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
@ -336,8 +393,9 @@ struct State {
|
|||
WhoReactedIds(item, {}, context),
|
||||
WhoReadIds(item, context)
|
||||
) | rpl::map([=](PeersWithReactions &&reacted, Peers &&read) {
|
||||
if (reacted.unknown || read.unknown) {
|
||||
return PeersWithReactions{ .unknown = true };
|
||||
if (reacted.state == WhoReadState::Unknown
|
||||
|| read.state == WhoReadState::Unknown) {
|
||||
return PeersWithReactions{ .state = WhoReadState::Unknown};
|
||||
}
|
||||
auto &list = reacted.list;
|
||||
for (const auto &peerWithDate : read.list) {
|
||||
|
@ -531,16 +589,17 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
|
|||
std::move(
|
||||
idsWithReactions
|
||||
) | rpl::start_with_next([=](PeersWithReactions &&peers) {
|
||||
if (peers.unknown) {
|
||||
if (peers.state == WhoReadState::Unknown) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{
|
||||
.type = state->current.type,
|
||||
.fullReactionsCount = state->current.fullReactionsCount,
|
||||
.fullReadCount = state->current.fullReadCount,
|
||||
.unknown = true,
|
||||
.state = WhoReadState::Unknown,
|
||||
});
|
||||
return;
|
||||
}
|
||||
state->current.state = peers.state;
|
||||
state->current.fullReadCount = int(peers.read.size());
|
||||
state->current.fullReactionsCount = peers.fullReactionsCount;
|
||||
if (whoReadIds) {
|
||||
|
@ -631,6 +690,22 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
|||
}
|
||||
const auto history = item->history();
|
||||
const auto peer = history->peer;
|
||||
if (const auto user = peer->asUser()) {
|
||||
if (user->isSelf()
|
||||
|| user->isBot()
|
||||
|| user->isServiceUser()
|
||||
|| user->readDatesPrivate()) {
|
||||
return false;
|
||||
}
|
||||
const auto &appConfig = peer->session().account().appConfig();
|
||||
const auto expirePeriod = appConfig.get<int>(
|
||||
"pm_read_date_expire_period",
|
||||
7 * 86400);
|
||||
if (item->date() + int64(expirePeriod) <= int64(base::unixtime::now())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const auto chat = peer->asChat();
|
||||
const auto megagroup = peer->asMegagroup();
|
||||
if ((!chat && !megagroup)
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "api/api_attached_stickers.h"
|
||||
#include "api/api_editing.h"
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "api/api_polls.h"
|
||||
#include "api/api_report.h"
|
||||
#include "api/api_ringtones.h"
|
||||
|
@ -41,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/delete_messages_box.h"
|
||||
#include "boxes/report_messages_box.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "boxes/sticker_set_box.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "boxes/translate_box.h"
|
||||
|
@ -62,6 +64,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/click_handler_types.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
@ -1265,11 +1268,23 @@ void AddWhoReactedAction(
|
|||
not_null<Window::SessionController*> controller) {
|
||||
const auto whoReadIds = std::make_shared<Api::WhoReadList>();
|
||||
const auto weak = Ui::MakeWeak(menu.get());
|
||||
const auto user = item->history()->peer;
|
||||
const auto participantChosen = [=](uint64 id) {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->hideMenu();
|
||||
}
|
||||
controller->showPeerInfo(PeerId(id));
|
||||
if (id) {
|
||||
controller->showPeerInfo(PeerId(id));
|
||||
} else {
|
||||
const auto type = ShowOrPremium::ReadTime;
|
||||
auto box = Box(ShowOrPremiumBox, type, user->shortName(), [=] {
|
||||
const auto api = &controller->session().api();
|
||||
api->globalPrivacy().updateHideReadTime({});
|
||||
}, [=] {
|
||||
Settings::ShowPremium(controller, u"revtime_hidden"_q);
|
||||
});
|
||||
controller->show(std::move(box));
|
||||
}
|
||||
};
|
||||
const auto showAllChosen = [=, itemId = item->fullId()]{
|
||||
// Pressing on an item that has a submenu doesn't hide it :(
|
||||
|
@ -1396,7 +1411,7 @@ void ShowWhoReactedMenu(
|
|||
context,
|
||||
st::defaultWhoRead
|
||||
) | rpl::filter([=](const Ui::WhoReadContent &content) {
|
||||
return !content.unknown;
|
||||
return content.state != Ui::WhoReadState::Unknown;
|
||||
}) | rpl::start_with_next([=, &lifetime](Ui::WhoReadContent &&content) {
|
||||
const auto creating = !*menu;
|
||||
const auto refillTop = [=] {
|
||||
|
|
|
@ -178,7 +178,7 @@ Action::Action(
|
|||
) | rpl::start_with_next([=](WhoReadContent &&content) {
|
||||
checkAppeared();
|
||||
const auto changed = (_content.participants != content.participants)
|
||||
|| (_content.unknown != content.unknown);
|
||||
|| (_content.state != content.state);
|
||||
_content = content;
|
||||
if (changed) {
|
||||
PostponeCall(this, [=] { populateSubmenu(); });
|
||||
|
@ -219,6 +219,10 @@ Action::Action(
|
|||
if (const auto onstack = _showAllChosen) {
|
||||
onstack();
|
||||
}
|
||||
} else if (_content.state == WhoReadState::MyHidden) {
|
||||
if (const auto onstack = _participantChosen) {
|
||||
onstack(0);
|
||||
}
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
|
@ -379,8 +383,13 @@ void Action::refreshText() {
|
|||
const auto count = std::max(_content.fullReactionsCount, usersCount);
|
||||
_text.setMarkedText(
|
||||
_st.itemStyle,
|
||||
{ (_content.unknown
|
||||
{ ((_content.state == WhoReadState::Unknown)
|
||||
? tr::lng_context_seen_loading(tr::now)
|
||||
: (_content.state == WhoReadState::MyHidden)
|
||||
? tr::lng_context_read_show(tr::now)
|
||||
: (_content.state == WhoReadState::HisHidden
|
||||
|| _content.state == WhoReadState::TooOld)
|
||||
? tr::lng_context_read_hidden(tr::now)
|
||||
: (usersCount == 1)
|
||||
? _content.participants.front().name
|
||||
: (_content.fullReactionsCount > 0
|
||||
|
@ -431,7 +440,8 @@ void Action::refreshDimensions() {
|
|||
}
|
||||
|
||||
bool Action::isEnabled() const {
|
||||
return !_content.participants.empty();
|
||||
return !_content.participants.empty()
|
||||
|| (_content.state == WhoReadState::MyHidden);
|
||||
}
|
||||
|
||||
not_null<QAction*> Action::action() const {
|
||||
|
|
|
@ -38,13 +38,21 @@ enum class WhoReadType {
|
|||
Reacted,
|
||||
};
|
||||
|
||||
enum class WhoReadState : uchar {
|
||||
Empty,
|
||||
Unknown,
|
||||
MyHidden,
|
||||
HisHidden,
|
||||
TooOld,
|
||||
};
|
||||
|
||||
struct WhoReadContent {
|
||||
std::vector<WhoReadParticipant> participants;
|
||||
WhoReadType type = WhoReadType::Seen;
|
||||
QString singleCustomEntityData;
|
||||
int fullReactionsCount = 0;
|
||||
int fullReadCount = 0;
|
||||
bool unknown = false;
|
||||
WhoReadState state = WhoReadState::Empty;
|
||||
};
|
||||
|
||||
[[nodiscard]] base::unique_qptr<Menu::ItemBase> WhoReactedContextAction(
|
||||
|
|
Loading…
Add table
Reference in a new issue