mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-25 15:03:03 +02:00
Allow enabling direct messages in channels.
This commit is contained in:
parent
23eedb468f
commit
d3f9a84a0a
39 changed files with 685 additions and 242 deletions
|
@ -1886,6 +1886,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_manage_linked_channel_posted" = "All new posts from this channel are forwarded to the group.";
|
||||
"lng_manage_discussion_group_warning" = "\"Chat history for new members\" will be switched to **Visible**.";
|
||||
|
||||
"lng_manage_monoforum" = "Direct Messages";
|
||||
"lng_manage_monoforum_off" = "Off";
|
||||
"lng_manage_monoforum_free" = "Free";
|
||||
"lng_manage_monoforum_allow" = "Allow Direct Messages";
|
||||
"lng_manage_monoforum_about" = "Allow users to write direct private messages to your channel, with the option to charge a fee for every message.";
|
||||
"lng_manage_monoforum_price_about" = "Charge users for the ability to write a direct message to your channel. Your channel will receive {percent} of the selected fee ({amount}) for each incoming message.";
|
||||
|
||||
"lng_manage_history_visibility_title" = "Chat history for new members";
|
||||
"lng_manage_history_visibility_shown" = "Visible";
|
||||
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
||||
|
@ -2243,6 +2250,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_message_price_free" = "Messages are now free in this group.";
|
||||
"lng_action_message_price_paid#one" = "Messages now cost {count} Star each in this group.";
|
||||
"lng_action_message_price_paid#other" = "Messages now cost {count} Stars each in this group.";
|
||||
"lng_action_direct_messages_enabled" = "Channel enabled Direct Messages.";
|
||||
"lng_action_direct_messages_paid#one" = "Channel allows Direct Messages for {count} Star each.";
|
||||
"lng_action_direct_messages_paid#other" = "Channel allows Direct Messages for {count} Stars each";
|
||||
"lng_action_direct_messages_disabled" = "Channel disabled Direct Messages.";
|
||||
"lng_you_paid_stars#one" = "You paid {count} Star.";
|
||||
"lng_you_paid_stars#other" = "You paid {count} Stars.";
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -381,6 +382,9 @@ void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
|||
}
|
||||
|
||||
void ApiWrap::savePinnedOrder(not_null<Data::SavedMessages*> saved) {
|
||||
if (saved->parentChat()) {
|
||||
return;
|
||||
}
|
||||
const auto &order = _session->data().pinnedChatsOrder(saved);
|
||||
const auto input = [](Dialogs::Key key) {
|
||||
if (const auto sublist = key.sublist()) {
|
||||
|
|
|
@ -1168,12 +1168,13 @@ rpl::producer<int> SetupChargeSlider(
|
|||
struct State {
|
||||
rpl::variable<int> stars;
|
||||
};
|
||||
const auto group = !peer->isUser();
|
||||
const auto broadcast = peer->isBroadcast();
|
||||
const auto group = !broadcast && !peer->isUser();
|
||||
const auto state = container->lifetime().make_state<State>();
|
||||
const auto chargeStars = savedValue ? savedValue : kDefaultChargeStars;
|
||||
state->stars = chargeStars;
|
||||
|
||||
Ui::AddSubsectionTitle(container, group
|
||||
Ui::AddSubsectionTitle(container, (group || broadcast)
|
||||
? tr::lng_rights_charge_price()
|
||||
: tr::lng_messages_privacy_price());
|
||||
|
||||
|
@ -1225,7 +1226,9 @@ rpl::producer<int> SetupChargeSlider(
|
|||
const auto percent = peer->session().appConfig().paidMessageCommission();
|
||||
Ui::AddDividerText(
|
||||
container,
|
||||
(group
|
||||
(broadcast
|
||||
? tr::lng_manage_monoforum_price_about
|
||||
: group
|
||||
? tr::lng_rights_charge_price_about
|
||||
: tr::lng_messages_privacy_price_about)(
|
||||
lt_percent,
|
||||
|
@ -1235,3 +1238,54 @@ rpl::producer<int> SetupChargeSlider(
|
|||
|
||||
return state->stars.value();
|
||||
}
|
||||
|
||||
void EditDirectMessagesPriceBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<ChannelData*> channel,
|
||||
std::optional<int> savedValue,
|
||||
Fn<void(std::optional<int>)> callback) {
|
||||
box->setTitle(tr::lng_manage_monoforum());
|
||||
|
||||
const auto toggle = box->addRow(object_ptr<Ui::SettingsButton>(
|
||||
box,
|
||||
tr::lng_manage_monoforum_allow(),
|
||||
st::settingsButtonNoIcon
|
||||
), {})->toggleOn(rpl::single(savedValue.has_value()));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
|
||||
Ui::AddDividerText(
|
||||
box->verticalLayout(),
|
||||
tr::lng_manage_monoforum_about());
|
||||
|
||||
const auto wrap = box->addRow(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
wrap->toggle(savedValue.has_value(), anim::type::instant);
|
||||
wrap->toggleOn(toggle->toggledChanges());
|
||||
|
||||
const auto result = box->lifetime().make_state<int>(
|
||||
savedValue.value_or(0));
|
||||
|
||||
const auto inner = wrap->entity();
|
||||
Ui::AddSkip(inner);
|
||||
SetupChargeSlider(
|
||||
inner,
|
||||
channel,
|
||||
savedValue.value_or(0)
|
||||
) | rpl::start_with_next([=](int stars) {
|
||||
*result = stars;
|
||||
}, box->lifetime());
|
||||
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
callback(toggle->toggled() ? *result : std::optional<int>());
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -174,3 +174,9 @@ void EditMessagesPrivacyBox(
|
|||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
int savedValue);
|
||||
|
||||
void EditDirectMessagesPriceBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<ChannelData*> channel,
|
||||
std::optional<int> savedValue,
|
||||
Fn<void(std::optional<int>)> callback);
|
||||
|
|
|
@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/peers/replace_boost_box.h"
|
||||
#include "boxes/peers/verify_peers_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/edit_privacy_box.h" // EditDirectMessagesPriceBox
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "boxes/username_box.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
|
@ -220,28 +221,41 @@ void SaveSlowmodeSeconds(
|
|||
}
|
||||
|
||||
void SaveStarsPerMessage(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<ChannelData*> channel,
|
||||
int starsPerMessage,
|
||||
Fn<void()> done) {
|
||||
Fn<void(bool)> done) {
|
||||
const auto api = &channel->session().api();
|
||||
const auto key = Api::RequestKey("stars_per_message", channel->id);
|
||||
|
||||
const auto broadcast = channel->isBroadcast();
|
||||
|
||||
using Flag = MTPchannels_UpdatePaidMessagesPrice::Flag;
|
||||
const auto broadcastAllowed = broadcast && (starsPerMessage >= 0);
|
||||
const auto requestId = api->request(MTPchannels_UpdatePaidMessagesPrice(
|
||||
MTP_flags(0), // #TODO Support broadcast_messages_allowed flag in UI
|
||||
MTP_flags(broadcastAllowed
|
||||
? Flag::f_broadcast_messages_allowed
|
||||
: Flag(0)),
|
||||
channel->inputChannel,
|
||||
MTP_long(starsPerMessage)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->clearModifyRequest(key);
|
||||
api->applyUpdates(result);
|
||||
channel->setStarsPerMessage(starsPerMessage);
|
||||
done();
|
||||
if (!broadcast) {
|
||||
channel->setStarsPerMessage(starsPerMessage);
|
||||
}
|
||||
done(true);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
api->clearModifyRequest(key);
|
||||
if (error.type() != u"CHAT_NOT_MODIFIED"_q) {
|
||||
return;
|
||||
show->showToast(error.type());
|
||||
done(false);
|
||||
} else {
|
||||
if (!broadcast) {
|
||||
channel->setStarsPerMessage(starsPerMessage);
|
||||
}
|
||||
done(true);
|
||||
}
|
||||
channel->setStarsPerMessage(starsPerMessage);
|
||||
done();
|
||||
}).send();
|
||||
|
||||
api->registerModifyRequest(key, requestId);
|
||||
|
@ -281,6 +295,7 @@ void SaveBoostsUnrestrict(
|
|||
void ShowEditPermissions(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer) {
|
||||
const auto show = navigation->uiShow();
|
||||
auto createBox = [=](not_null<Ui::GenericBox*> box) {
|
||||
const auto saving = box->lifetime().make_state<int>(0);
|
||||
const auto save = [=](
|
||||
|
@ -299,7 +314,10 @@ void ShowEditPermissions(
|
|||
channel,
|
||||
result.boostsUnrestrict,
|
||||
close);
|
||||
SaveStarsPerMessage(channel, result.starsPerMessage, close);
|
||||
const auto price = result.starsPerMessage;
|
||||
SaveStarsPerMessage(show, channel, price, [=](bool ok) {
|
||||
close();
|
||||
});
|
||||
}
|
||||
};
|
||||
auto done = [=](EditPeerPermissionsBoxResult result) {
|
||||
|
@ -366,6 +384,7 @@ private:
|
|||
std::optional<bool> joinToWrite;
|
||||
std::optional<bool> requestToJoin;
|
||||
std::optional<ChannelData*> discussionLink;
|
||||
std::optional<int> starsPerDirectMessage;
|
||||
};
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
|
||||
|
@ -382,8 +401,10 @@ private:
|
|||
void showEditPeerTypeBox(
|
||||
std::optional<rpl::producer<QString>> error = {});
|
||||
void showEditDiscussionLinkBox();
|
||||
void showEditDirectMessagesBox();
|
||||
void fillPrivacyTypeButton();
|
||||
void fillDiscussionLinkButton();
|
||||
void fillDirectMessagesButton();
|
||||
//void fillInviteLinkButton();
|
||||
void fillForumButton();
|
||||
void fillColorIndexButton();
|
||||
|
@ -412,6 +433,7 @@ private:
|
|||
[[nodiscard]] bool validateUsernamesOrder(Saving &to) const;
|
||||
[[nodiscard]] bool validateUsername(Saving &to) const;
|
||||
[[nodiscard]] bool validateDiscussionLink(Saving &to) const;
|
||||
[[nodiscard]] bool validateDirectMessagesPrice(Saving &to) const;
|
||||
[[nodiscard]] bool validateTitle(Saving &to) const;
|
||||
[[nodiscard]] bool validateDescription(Saving &to) const;
|
||||
[[nodiscard]] bool validateHistoryVisibility(Saving &to) const;
|
||||
|
@ -426,6 +448,7 @@ private:
|
|||
void saveUsernamesOrder();
|
||||
void saveUsername();
|
||||
void saveDiscussionLink();
|
||||
void saveDirectMessagesPrice();
|
||||
void saveTitle();
|
||||
void saveDescription();
|
||||
void saveHistoryVisibility();
|
||||
|
@ -454,6 +477,7 @@ private:
|
|||
std::optional<ChannelData*> _discussionLinkSavedValue;
|
||||
ChannelData *_discussionLinkOriginalValue = nullptr;
|
||||
bool _channelHasLocationOriginalValue = false;
|
||||
std::optional<rpl::variable<int>> _starsPerDirectMessageSavedValue;
|
||||
std::optional<HistoryVisibility> _historyVisibilitySavedValue;
|
||||
std::optional<EditPeerTypeData> _typeDataSavedValue;
|
||||
std::optional<bool> _forumSavedValue;
|
||||
|
@ -918,6 +942,20 @@ void Controller::showEditDiscussionLinkBox() {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void Controller::showEditDirectMessagesBox() {
|
||||
Expects(_peer->isBroadcast());
|
||||
Expects(_starsPerDirectMessageSavedValue.has_value());
|
||||
|
||||
const auto stars = _starsPerDirectMessageSavedValue->current();
|
||||
_navigation->parentController()->show(Box(
|
||||
EditDirectMessagesPriceBox,
|
||||
_peer->asChannel(),
|
||||
(stars >= 0) ? stars : std::optional<int>(),
|
||||
[=](std::optional<int> value) {
|
||||
*_starsPerDirectMessageSavedValue = value.value_or(-1);
|
||||
}));
|
||||
}
|
||||
|
||||
void Controller::fillPrivacyTypeButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
|
@ -983,9 +1021,11 @@ void Controller::fillPrivacyTypeButton() {
|
|||
void Controller::fillDiscussionLinkButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
_discussionLinkSavedValue = _discussionLinkOriginalValue = _peer->isChannel()
|
||||
? _peer->asChannel()->discussionLink()
|
||||
: nullptr;
|
||||
_discussionLinkSavedValue
|
||||
= _discussionLinkOriginalValue
|
||||
= (_peer->isChannel()
|
||||
? _peer->asChannel()->discussionLink()
|
||||
: nullptr);
|
||||
|
||||
const auto isGroup = (_peer->isChat() || _peer->isMegagroup());
|
||||
auto text = !isGroup
|
||||
|
@ -1019,6 +1059,33 @@ void Controller::fillDiscussionLinkButton() {
|
|||
{ isGroup ? &st::menuIconChannel : &st::menuIconGroups });
|
||||
_discussionLinkUpdates.fire_copy(*_discussionLinkSavedValue);
|
||||
}
|
||||
|
||||
void Controller::fillDirectMessagesButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
if (!_peer->isBroadcast() || !_peer->asChannel()->canEditInformation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto monoforumLink = _peer->asChannel()->monoforumLink();
|
||||
_starsPerDirectMessageSavedValue = rpl::variable<int>(
|
||||
monoforumLink ? monoforumLink->starsPerMessage() : -1);
|
||||
|
||||
auto label = _starsPerDirectMessageSavedValue->value(
|
||||
) | rpl::map([](int starsPerMessage) {
|
||||
return (starsPerMessage < 0)
|
||||
? tr::lng_manage_monoforum_off()
|
||||
: !starsPerMessage
|
||||
? tr::lng_manage_monoforum_free()
|
||||
: rpl::single(Lang::FormatCountDecimal(starsPerMessage));
|
||||
}) | rpl::flatten_latest();
|
||||
AddButtonWithText(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_monoforum(),
|
||||
std::move(label),
|
||||
[=] { showEditDirectMessagesBox(); },
|
||||
{ &st::menuIconChatBubble });
|
||||
}
|
||||
//
|
||||
//void Controller::fillInviteLinkButton() {
|
||||
// Expects(_controls.buttonsLayout != nullptr);
|
||||
|
@ -1359,6 +1426,8 @@ void Controller::fillManageSection() {
|
|||
const auto canViewOrEditDiscussionLink = isChannel
|
||||
&& (channel->discussionLink()
|
||||
|| (channel->isBroadcast() && channel->canEditInformation()));
|
||||
const auto canEditDirectMessages = isChannel
|
||||
&& (channel->isBroadcast() && channel->canEditInformation());
|
||||
|
||||
::AddSkip(_controls.buttonsLayout, 0);
|
||||
|
||||
|
@ -1370,6 +1439,9 @@ void Controller::fillManageSection() {
|
|||
if (canViewOrEditDiscussionLink) {
|
||||
fillDiscussionLinkButton();
|
||||
}
|
||||
if (canEditDirectMessages) {
|
||||
fillDirectMessagesButton();
|
||||
}
|
||||
if (canEditPreHistoryHidden) {
|
||||
fillHistoryVisibilityButton();
|
||||
}
|
||||
|
@ -1973,6 +2045,7 @@ std::optional<Controller::Saving> Controller::validate() const {
|
|||
if (validateUsernamesOrder(result)
|
||||
&& validateUsername(result)
|
||||
&& validateDiscussionLink(result)
|
||||
&& validateDirectMessagesPrice(result)
|
||||
&& validateTitle(result)
|
||||
&& validateDescription(result)
|
||||
&& validateHistoryVisibility(result)
|
||||
|
@ -2022,6 +2095,14 @@ bool Controller::validateDiscussionLink(Saving &to) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Controller::validateDirectMessagesPrice(Saving &to) const {
|
||||
if (!_starsPerDirectMessageSavedValue) {
|
||||
return true;
|
||||
}
|
||||
to.starsPerDirectMessage = _starsPerDirectMessageSavedValue->current();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::validateTitle(Saving &to) const {
|
||||
if (!_controls.title) {
|
||||
return true;
|
||||
|
@ -2120,6 +2201,7 @@ void Controller::save() {
|
|||
pushSaveStage([=] { saveUsernamesOrder(); });
|
||||
pushSaveStage([=] { saveUsername(); });
|
||||
pushSaveStage([=] { saveDiscussionLink(); });
|
||||
pushSaveStage([=] { saveDirectMessagesPrice(); });
|
||||
pushSaveStage([=] { saveTitle(); });
|
||||
pushSaveStage([=] { saveDescription(); });
|
||||
pushSaveStage([=] { saveHistoryVisibility(); });
|
||||
|
@ -2277,6 +2359,30 @@ void Controller::saveDiscussionLink() {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void Controller::saveDirectMessagesPrice() {
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!channel) {
|
||||
return continueSave();
|
||||
}
|
||||
const auto monoforumLink = channel->monoforumLink();
|
||||
const auto current = monoforumLink ? monoforumLink->starsPerMessage() : -1;
|
||||
const auto desired = _savingData.starsPerDirectMessage
|
||||
? *_savingData.starsPerDirectMessage
|
||||
: current;
|
||||
if (desired == current) {
|
||||
return continueSave();
|
||||
}
|
||||
const auto show = _navigation->uiShow();
|
||||
const auto done = [=](bool ok) {
|
||||
if (ok) {
|
||||
continueSave();
|
||||
} else {
|
||||
cancelSave();
|
||||
}
|
||||
};
|
||||
SaveStarsPerMessage(show, channel, desired, crl::guard(this, done));
|
||||
}
|
||||
|
||||
void Controller::saveTitle() {
|
||||
if (!_savingData.title || *_savingData.title == _peer->name()) {
|
||||
return continueSave();
|
||||
|
|
|
@ -907,6 +907,7 @@ void PinsLimitBox(
|
|||
limits.dialogsPinnedPremium(),
|
||||
PinsCount(session->data().chatsList()));
|
||||
}
|
||||
|
||||
void SublistsPinsLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session) {
|
||||
|
|
|
@ -871,6 +871,10 @@ historyGiftToChannel: IconButton(defaultIconButton) {
|
|||
rippleAreaSize: 40px;
|
||||
ripple: universalRippleAnimation;
|
||||
}
|
||||
historyDirectMessage: IconButton(historyGiftToChannel) {
|
||||
icon: icon{{ "menu/chat_bubble", windowActiveTextFg }};
|
||||
iconOver: icon{{ "menu/chat_bubble", windowActiveTextFg }};
|
||||
}
|
||||
historyUnblock: FlatButton(historyComposeButton) {
|
||||
color: attentionButtonFg;
|
||||
overColor: attentionButtonFgOver;
|
||||
|
|
|
@ -112,12 +112,13 @@ struct PeerUpdate {
|
|||
StickersSet = (1ULL << 46),
|
||||
EmojiSet = (1ULL << 47),
|
||||
DiscussionLink = (1ULL << 48),
|
||||
ChannelLocation = (1ULL << 49),
|
||||
Slowmode = (1ULL << 50),
|
||||
GroupCall = (1ULL << 51),
|
||||
MonoforumLink = (1ULL << 49),
|
||||
ChannelLocation = (1ULL << 50),
|
||||
Slowmode = (1ULL << 51),
|
||||
GroupCall = (1ULL << 52),
|
||||
|
||||
// For iteration
|
||||
LastUsedBit = (1ULL << 51),
|
||||
LastUsedBit = (1ULL << 52),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_histories.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_wall_paper.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -89,6 +90,29 @@ std::unique_ptr<Data::Forum> MegagroupInfo::takeForumData() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void MegagroupInfo::ensureMonoforum(not_null<ChannelData*> that) {
|
||||
if (!_monoforum) {
|
||||
const auto history = that->owner().history(that);
|
||||
_monoforum = std::make_unique<Data::SavedMessages>(
|
||||
&that->owner(),
|
||||
that);
|
||||
history->monoforumChanged(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Data::SavedMessages *MegagroupInfo::monoforum() const {
|
||||
return _monoforum.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<Data::SavedMessages> MegagroupInfo::takeMonoforumData() {
|
||||
if (auto result = base::take(_monoforum)) {
|
||||
const auto history = result->owner().history(result->parentChat());
|
||||
history->monoforumChanged(result.get());
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
|
||||
: PeerData(owner, id)
|
||||
, inputChannel(
|
||||
|
@ -161,6 +185,12 @@ void ChannelData::setAccessHash(uint64 accessHash) {
|
|||
}
|
||||
|
||||
void ChannelData::setFlags(ChannelDataFlags which) {
|
||||
if (which & (Flag::Forum | Flag::Monoforum)) {
|
||||
which |= Flag::Megagroup;
|
||||
}
|
||||
if (which & Flag::Monoforum) {
|
||||
which &= ~Flag::Forum;
|
||||
}
|
||||
const auto diff = flags() ^ which;
|
||||
if ((which & Flag::Megagroup) && !mgInfo) {
|
||||
mgInfo = std::make_unique<MegagroupInfo>();
|
||||
|
@ -276,8 +306,9 @@ const ChannelLocation *ChannelData::getLocation() const {
|
|||
}
|
||||
|
||||
void ChannelData::setDiscussionLink(ChannelData *linked) {
|
||||
if (_discussionLink != linked) {
|
||||
if (_discussionLink != linked || !_discussionLinkKnown) {
|
||||
_discussionLink = linked;
|
||||
_discussionLinkKnown = true;
|
||||
if (const auto history = owner().historyLoaded(this)) {
|
||||
history->forceFullResize();
|
||||
}
|
||||
|
@ -286,11 +317,22 @@ void ChannelData::setDiscussionLink(ChannelData *linked) {
|
|||
}
|
||||
|
||||
ChannelData *ChannelData::discussionLink() const {
|
||||
return _discussionLink.value_or(nullptr);
|
||||
return _discussionLink;
|
||||
}
|
||||
|
||||
bool ChannelData::discussionLinkKnown() const {
|
||||
return _discussionLink.has_value();
|
||||
return _discussionLinkKnown;
|
||||
}
|
||||
|
||||
void ChannelData::setMonoforumLink(ChannelData *link) {
|
||||
if (_monoforumLink != link) {
|
||||
_monoforumLink = link;
|
||||
session().changes().peerUpdated(this, UpdateFlag::MonoforumLink);
|
||||
}
|
||||
}
|
||||
|
||||
ChannelData *ChannelData::monoforumLink() const {
|
||||
return _monoforumLink;
|
||||
}
|
||||
|
||||
void ChannelData::setMembersCount(int newMembersCount) {
|
||||
|
@ -1240,6 +1282,11 @@ void ApplyChannelUpdate(
|
|||
} else {
|
||||
channel->setDiscussionLink(nullptr);
|
||||
}
|
||||
if (const auto chat = update.vlinked_monoforum_id()) {
|
||||
channel->setMonoforumLink(channel->owner().channelLoaded(chat->v));
|
||||
} else {
|
||||
channel->setMonoforumLink(nullptr);
|
||||
}
|
||||
if (const auto history = channel->owner().historyLoaded(channel)) {
|
||||
if (const auto available = update.vavailable_min_id()) {
|
||||
history->clearUpTill(available->v);
|
||||
|
|
|
@ -16,6 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class ChannelData;
|
||||
|
||||
namespace Data {
|
||||
class Forum;
|
||||
class SavedMessages;
|
||||
} // namespace Data
|
||||
|
||||
struct ChannelLocation {
|
||||
QString address;
|
||||
Data::LocationPoint point;
|
||||
|
@ -74,6 +79,7 @@ enum class ChannelDataFlag : uint64 {
|
|||
StargiftsAvailable = (1ULL << 36),
|
||||
PaidMessagesAvailable = (1ULL << 37),
|
||||
AutoTranslation = (1ULL << 38),
|
||||
Monoforum = (1ULL << 39),
|
||||
};
|
||||
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
|
||||
using ChannelDataFlags = base::flags<ChannelDataFlag>;
|
||||
|
@ -118,6 +124,10 @@ public:
|
|||
[[nodiscard]] Data::Forum *forum() const;
|
||||
[[nodiscard]] std::unique_ptr<Data::Forum> takeForumData();
|
||||
|
||||
void ensureMonoforum(not_null<ChannelData*> that);
|
||||
[[nodiscard]] Data::SavedMessages *monoforum() const;
|
||||
[[nodiscard]] std::unique_ptr<Data::SavedMessages> takeMonoforumData();
|
||||
|
||||
std::deque<not_null<UserData*>> lastParticipants;
|
||||
base::flat_map<not_null<UserData*>, Admin> lastAdmins;
|
||||
base::flat_map<not_null<UserData*>, Restricted> lastRestricted;
|
||||
|
@ -154,6 +164,7 @@ private:
|
|||
ChannelLocation _location;
|
||||
Data::ChatBotCommands _botCommands;
|
||||
std::unique_ptr<Data::Forum> _forum;
|
||||
std::unique_ptr<Data::SavedMessages> _monoforum;
|
||||
int _starsPerMessage = 0;
|
||||
|
||||
friend class ChannelData;
|
||||
|
@ -301,6 +312,9 @@ public:
|
|||
[[nodiscard]] bool isForum() const {
|
||||
return flags() & Flag::Forum;
|
||||
}
|
||||
[[nodiscard]] bool isMonoforum() const {
|
||||
return flags() & Flag::Monoforum;
|
||||
}
|
||||
[[nodiscard]] bool hasUsername() const {
|
||||
return flags() & Flag::Username;
|
||||
}
|
||||
|
@ -413,6 +427,9 @@ public:
|
|||
[[nodiscard]] ChannelData *discussionLink() const;
|
||||
[[nodiscard]] bool discussionLinkKnown() const;
|
||||
|
||||
void setMonoforumLink(ChannelData *link);
|
||||
[[nodiscard]] ChannelData *monoforumLink() const;
|
||||
|
||||
void ptsInit(int32 pts) {
|
||||
_ptsWaiter.init(pts);
|
||||
}
|
||||
|
@ -510,6 +527,9 @@ public:
|
|||
[[nodiscard]] Data::Forum *forum() const {
|
||||
return mgInfo ? mgInfo->forum() : nullptr;
|
||||
}
|
||||
[[nodiscard]] Data::SavedMessages *monoforum() const {
|
||||
return mgInfo ? mgInfo->monoforum() : nullptr;
|
||||
}
|
||||
|
||||
void processTopics(const MTPVector<MTPForumTopic> &topics);
|
||||
|
||||
|
@ -546,18 +566,11 @@ private:
|
|||
std::vector<Data::UnavailableReason> &&reasons) override;
|
||||
|
||||
Flags _flags = ChannelDataFlags(Flag::Forbidden);
|
||||
int _peerGiftsCount = 0;
|
||||
|
||||
PtsWaiter _ptsWaiter;
|
||||
|
||||
Data::UsernamesInfo _username;
|
||||
|
||||
int _membersCount = -1;
|
||||
int _adminsCount = 1;
|
||||
int _restrictedCount = 0;
|
||||
int _kickedCount = 0;
|
||||
int _pendingRequestsCount = 0;
|
||||
int _levelHint = 0;
|
||||
std::vector<UserId> _recentRequesters;
|
||||
MsgId _availableMinId = 0;
|
||||
|
||||
|
@ -570,7 +583,18 @@ private:
|
|||
std::vector<Data::UnavailableReason> _unavailableReasons;
|
||||
std::unique_ptr<InvitePeek> _invitePeek;
|
||||
QString _inviteLink;
|
||||
std::optional<ChannelData*> _discussionLink;
|
||||
|
||||
ChannelData *_discussionLink = nullptr;
|
||||
ChannelData *_monoforumLink = nullptr;
|
||||
bool _discussionLinkKnown = false;
|
||||
|
||||
int _peerGiftsCount = 0;
|
||||
int _membersCount = -1;
|
||||
int _adminsCount = 1;
|
||||
int _restrictedCount = 0;
|
||||
int _kickedCount = 0;
|
||||
int _pendingRequestsCount = 0;
|
||||
int _levelHint = 0;
|
||||
|
||||
Data::AllowedReactions _allowedReactions;
|
||||
|
||||
|
|
|
@ -159,7 +159,8 @@ bool CanSendAnyOf(
|
|||
using Flag = ChannelDataFlag;
|
||||
const auto allowed = channel->amIn()
|
||||
|| ((channel->flags() & Flag::HasLink)
|
||||
&& !(channel->flags() & Flag::JoinToWrite));
|
||||
&& !(channel->flags() & Flag::JoinToWrite))
|
||||
|| channel->isMonoforum();
|
||||
if (!allowed || (forbidInForums && channel->isForum())) {
|
||||
return false;
|
||||
} else if (channel->canPostMessages()) {
|
||||
|
|
|
@ -1333,6 +1333,13 @@ bool PeerData::isForum() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::isMonoforum() const {
|
||||
if (const auto channel = asChannel()) {
|
||||
return channel->isMonoforum();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::isGigagroup() const {
|
||||
if (const auto channel = asChannel()) {
|
||||
return channel->isGigagroup();
|
||||
|
@ -1416,6 +1423,13 @@ Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Data::SavedMessages *PeerData::monoforum() const {
|
||||
if (const auto channel = asChannel()) {
|
||||
return channel->monoforum();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PeerData::allowsForwarding() const {
|
||||
if (isUser()) {
|
||||
return true;
|
||||
|
|
|
@ -37,6 +37,7 @@ class Forum;
|
|||
class ForumTopic;
|
||||
class Session;
|
||||
class GroupCall;
|
||||
class SavedMessages;
|
||||
struct ReactionId;
|
||||
class WallPaper;
|
||||
|
||||
|
@ -232,6 +233,7 @@ public:
|
|||
[[nodiscard]] bool isMegagroup() const;
|
||||
[[nodiscard]] bool isBroadcast() const;
|
||||
[[nodiscard]] bool isForum() const;
|
||||
[[nodiscard]] bool isMonoforum() const;
|
||||
[[nodiscard]] bool isGigagroup() const;
|
||||
[[nodiscard]] bool isRepliesChat() const;
|
||||
[[nodiscard]] bool isVerifyCodes() const;
|
||||
|
@ -257,6 +259,8 @@ public:
|
|||
[[nodiscard]] Data::Forum *forum() const;
|
||||
[[nodiscard]] Data::ForumTopic *forumTopicFor(MsgId rootId) const;
|
||||
|
||||
[[nodiscard]] Data::SavedMessages *monoforum() const;
|
||||
|
||||
[[nodiscard]] Data::PeerNotifySettings ¬ify() {
|
||||
return _notify;
|
||||
}
|
||||
|
|
|
@ -269,6 +269,7 @@ inline auto DefaultRestrictionValue(
|
|||
| Flag::Left
|
||||
| Flag::Forum
|
||||
| Flag::JoinToWrite
|
||||
| Flag::Monoforum
|
||||
| Flag::HasLink
|
||||
| Flag::Forbidden
|
||||
| Flag::Creator
|
||||
|
@ -292,7 +293,8 @@ inline auto DefaultRestrictionValue(
|
|||
&& (flags & Flag::Forum);
|
||||
const auto allowed = !(flags & notAmInFlags)
|
||||
|| ((flags & Flag::HasLink)
|
||||
&& !(flags & Flag::JoinToWrite));
|
||||
&& !(flags & Flag::JoinToWrite))
|
||||
|| (flags & Flag::Monoforum);
|
||||
const auto restricted = sendRestriction
|
||||
| (defaultSendRestriction && !unrestrictedByBoosts);
|
||||
return allowed
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_saved_messages.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -25,12 +26,15 @@ constexpr auto kListFirstPerPage = 20;
|
|||
|
||||
} // namespace
|
||||
|
||||
SavedMessages::SavedMessages(not_null<Session*> owner)
|
||||
SavedMessages::SavedMessages(
|
||||
not_null<Session*> owner,
|
||||
ChannelData *parentChat)
|
||||
: _owner(owner)
|
||||
, _parentChat(parentChat)
|
||||
, _chatsList(
|
||||
&owner->session(),
|
||||
&_owner->session(),
|
||||
FilterId(),
|
||||
owner->maxPinnedChatsLimitValue(this))
|
||||
_owner->maxPinnedChatsLimitValue(this))
|
||||
, _loadMore([=] { sendLoadMoreRequests(); }) {
|
||||
}
|
||||
|
||||
|
@ -40,6 +44,10 @@ bool SavedMessages::supported() const {
|
|||
return !_unsupported;
|
||||
}
|
||||
|
||||
ChannelData *SavedMessages::parentChat() const {
|
||||
return _parentChat;
|
||||
}
|
||||
|
||||
Session &SavedMessages::owner() const {
|
||||
return *_owner;
|
||||
}
|
||||
|
@ -59,7 +67,11 @@ not_null<SavedSublist*> SavedMessages::sublist(not_null<PeerData*> peer) {
|
|||
}
|
||||
return _sublists.emplace(
|
||||
peer,
|
||||
std::make_unique<SavedSublist>(peer)).first->second.get();
|
||||
std::make_unique<SavedSublist>(this, peer)).first->second.get();
|
||||
}
|
||||
|
||||
rpl::producer<> SavedMessages::chatsListChanges() const {
|
||||
return _chatsListChanges.events();
|
||||
}
|
||||
|
||||
void SavedMessages::loadMore() {
|
||||
|
@ -78,10 +90,12 @@ void SavedMessages::sendLoadMore() {
|
|||
} else if (!_pinnedLoaded) {
|
||||
loadPinned();
|
||||
}
|
||||
using Flag = MTPmessages_GetSavedDialogs::Flag;
|
||||
_loadMoreRequestId = _owner->session().api().request(
|
||||
MTPmessages_GetSavedDialogs(
|
||||
MTP_flags(MTPmessages_GetSavedDialogs::Flag::f_exclude_pinned),
|
||||
MTPInputPeer(), // parent_peer
|
||||
MTP_flags(Flag::f_exclude_pinned
|
||||
| (_parentChat ? Flag::f_parent_peer : Flag(0))),
|
||||
_parentChat ? _parentChat->input : MTPInputPeer(),
|
||||
MTP_int(_offsetDate),
|
||||
MTP_int(_offsetId),
|
||||
_offsetPeer ? _offsetPeer->input : MTP_inputPeerEmpty(),
|
||||
|
@ -89,6 +103,7 @@ void SavedMessages::sendLoadMore() {
|
|||
MTP_long(0)) // hash
|
||||
).done([=](const MTPmessages_SavedDialogs &result) {
|
||||
apply(result, false);
|
||||
_chatsListChanges.fire({});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
|
@ -99,13 +114,14 @@ void SavedMessages::sendLoadMore() {
|
|||
}
|
||||
|
||||
void SavedMessages::loadPinned() {
|
||||
if (_pinnedRequestId) {
|
||||
if (_pinnedRequestId || parentChat()) {
|
||||
return;
|
||||
}
|
||||
_pinnedRequestId = _owner->session().api().request(
|
||||
MTPmessages_GetPinnedSavedDialogs()
|
||||
).done([=](const MTPmessages_SavedDialogs &result) {
|
||||
apply(result, true);
|
||||
_chatsListChanges.fire({});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
|
@ -124,10 +140,11 @@ void SavedMessages::sendLoadMore(not_null<SavedSublist*> sublist) {
|
|||
const auto offsetId = list.empty() ? MsgId(0) : list.back()->id;
|
||||
const auto offsetDate = list.empty() ? MsgId(0) : list.back()->date();
|
||||
const auto limit = offsetId ? kPerPage : kFirstPerPage;
|
||||
using Flag = MTPmessages_GetSavedHistory::Flag;
|
||||
const auto requestId = _owner->session().api().request(
|
||||
MTPmessages_GetSavedHistory(
|
||||
MTP_flags(0),
|
||||
MTPInputPeer(), // parent_peer
|
||||
MTP_flags(_parentChat ? Flag::f_parent_peer : Flag(0)),
|
||||
_parentChat ? _parentChat->input : MTPInputPeer(),
|
||||
sublist->peer()->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(offsetDate),
|
||||
|
@ -261,6 +278,8 @@ void SavedMessages::sendLoadMoreRequests() {
|
|||
}
|
||||
|
||||
void SavedMessages::apply(const MTPDupdatePinnedSavedDialogs &update) {
|
||||
Expects(!parentChat());
|
||||
|
||||
const auto list = update.vorder();
|
||||
if (!list) {
|
||||
loadPinned();
|
||||
|
@ -286,6 +305,8 @@ void SavedMessages::apply(const MTPDupdatePinnedSavedDialogs &update) {
|
|||
}
|
||||
|
||||
void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) {
|
||||
Expects(!parentChat());
|
||||
|
||||
update.vpeer().match([&](const MTPDdialogPeer &data) {
|
||||
const auto peer = _owner->peer(peerFromMTP(data.vpeer()));
|
||||
const auto i = _sublists.find(peer);
|
||||
|
@ -300,4 +321,8 @@ void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) {
|
|||
});
|
||||
}
|
||||
|
||||
rpl::lifetime &SavedMessages::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -20,10 +20,13 @@ class SavedSublist;
|
|||
|
||||
class SavedMessages final {
|
||||
public:
|
||||
explicit SavedMessages(not_null<Session*> owner);
|
||||
explicit SavedMessages(
|
||||
not_null<Session*> owner,
|
||||
ChannelData *parentChat = nullptr);
|
||||
~SavedMessages();
|
||||
|
||||
[[nodiscard]] bool supported() const;
|
||||
[[nodiscard]] ChannelData *parentChat() const;
|
||||
|
||||
[[nodiscard]] Session &owner() const;
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
|
@ -31,12 +34,16 @@ public:
|
|||
[[nodiscard]] not_null<Dialogs::MainList*> chatsList();
|
||||
[[nodiscard]] not_null<SavedSublist*> sublist(not_null<PeerData*> peer);
|
||||
|
||||
[[nodiscard]] rpl::producer<> chatsListChanges() const;
|
||||
|
||||
void loadMore();
|
||||
void loadMore(not_null<SavedSublist*> sublist);
|
||||
|
||||
void apply(const MTPDupdatePinnedSavedDialogs &update);
|
||||
void apply(const MTPDupdateSavedDialogPinned &update);
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
void loadPinned();
|
||||
void apply(const MTPmessages_SavedDialogs &result, bool pinned);
|
||||
|
@ -46,6 +53,7 @@ private:
|
|||
void sendLoadMoreRequests();
|
||||
|
||||
const not_null<Session*> _owner;
|
||||
ChannelData *_parentChat = nullptr;
|
||||
|
||||
Dialogs::MainList _chatsList;
|
||||
base::flat_map<
|
||||
|
@ -64,9 +72,13 @@ private:
|
|||
base::flat_set<not_null<SavedSublist*>> _loadMoreSublistsScheduled;
|
||||
bool _loadMoreScheduled = false;
|
||||
|
||||
rpl::event_stream<> _chatsListChanges;
|
||||
|
||||
bool _pinnedLoaded = false;
|
||||
bool _unsupported = false;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -17,13 +17,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace Data {
|
||||
|
||||
SavedSublist::SavedSublist(not_null<PeerData*> peer)
|
||||
SavedSublist::SavedSublist(
|
||||
not_null<SavedMessages*> parent,
|
||||
not_null<PeerData*> peer)
|
||||
: Entry(&peer->owner(), Dialogs::Entry::Type::SavedSublist)
|
||||
, _parent(parent)
|
||||
, _history(peer->owner().history(peer)) {
|
||||
}
|
||||
|
||||
SavedSublist::~SavedSublist() = default;
|
||||
|
||||
not_null<SavedMessages*> SavedSublist::parent() const {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
ChannelData *SavedSublist::parentChat() const {
|
||||
return _parent->parentChat();
|
||||
}
|
||||
|
||||
not_null<History*> SavedSublist::history() const {
|
||||
return _history;
|
||||
}
|
||||
|
@ -101,9 +112,7 @@ void SavedSublist::removeOne(not_null<HistoryItem*> item) {
|
|||
updateChatListExistence();
|
||||
} else {
|
||||
updateChatListEntry();
|
||||
crl::on_main(this, [=] {
|
||||
owner().savedMessages().loadMore(this);
|
||||
});
|
||||
crl::on_main(this, [=] { _parent->loadMore(this); });
|
||||
}
|
||||
} else {
|
||||
setChatListTimeId(_items.front()->date());
|
||||
|
|
|
@ -16,12 +16,15 @@ class History;
|
|||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class SavedMessages;
|
||||
|
||||
class SavedSublist final : public Dialogs::Entry {
|
||||
public:
|
||||
explicit SavedSublist(not_null<PeerData*> peer);
|
||||
SavedSublist(not_null<SavedMessages*> parent,not_null<PeerData*> peer);
|
||||
~SavedSublist();
|
||||
|
||||
[[nodiscard]] not_null<SavedMessages*> parent() const;
|
||||
[[nodiscard]] ChannelData *parentChat() const;
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||
[[nodiscard]] bool isHiddenAuthor() const;
|
||||
|
@ -72,6 +75,7 @@ private:
|
|||
void allowChatListMessageResolve();
|
||||
void resolveChatListMessageGroup();
|
||||
|
||||
const not_null<SavedMessages*> _parent;
|
||||
const not_null<History*> _history;
|
||||
|
||||
std::vector<not_null<HistoryItem*>> _items;
|
||||
|
|
|
@ -967,7 +967,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
|||
| ((!minimal && !data.is_stories_hidden_min())
|
||||
? Flag::StoriesHidden
|
||||
: Flag())
|
||||
| Flag::AutoTranslation;
|
||||
| Flag::AutoTranslation
|
||||
| Flag::Monoforum;
|
||||
const auto storiesState = minimal
|
||||
? std::optional<Data::Stories::PeerSourceState>()
|
||||
: data.is_stories_unavailable()
|
||||
|
@ -1007,7 +1008,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
|||
&& data.is_stories_hidden())
|
||||
? Flag::StoriesHidden
|
||||
: Flag())
|
||||
| (data.is_autotranslation() ? Flag::AutoTranslation : Flag());
|
||||
| (data.is_autotranslation() ? Flag::AutoTranslation : Flag())
|
||||
| (data.is_monoforum() ? Flag::Monoforum : Flag());
|
||||
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
|
||||
channel->setBotVerifyDetailsIcon(
|
||||
data.vbot_verification_icon().value_or_empty());
|
||||
|
@ -2310,6 +2312,9 @@ void Session::applyDialog(
|
|||
|
||||
bool Session::pinnedCanPin(not_null<Dialogs::Entry*> entry) const {
|
||||
if ([[maybe_unused]] const auto sublist = entry->asSublist()) {
|
||||
if (sublist->parentChat()) {
|
||||
return false;
|
||||
}
|
||||
const auto saved = &savedMessages();
|
||||
return pinnedChatsOrder(saved).size() < pinnedChatsLimit(saved);
|
||||
} else if (const auto topic = entry->asTopic()) {
|
||||
|
@ -2351,6 +2356,9 @@ int Session::pinnedChatsLimit(not_null<Data::Forum*> forum) const {
|
|||
}
|
||||
|
||||
int Session::pinnedChatsLimit(not_null<Data::SavedMessages*> saved) const {
|
||||
if (saved->parentChat()) {
|
||||
return 0;
|
||||
}
|
||||
const auto limits = Data::PremiumLimits(_session);
|
||||
return limits.savedSublistsPinnedCurrent();
|
||||
}
|
||||
|
@ -2391,6 +2399,9 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
|
|||
|
||||
rpl::producer<int> Session::maxPinnedChatsLimitValue(
|
||||
not_null<SavedMessages*> saved) const {
|
||||
if (saved->parentChat()) {
|
||||
return rpl::single(0);
|
||||
}
|
||||
// Premium limit from appconfig.
|
||||
// We always use premium limit in the MainList limit producer,
|
||||
// because it slices the list to that limit. We don't want to slice
|
||||
|
@ -4563,12 +4574,12 @@ not_null<Folder*> Session::processFolder(const MTPDfolder &data) {
|
|||
|
||||
not_null<Dialogs::MainList*> Session::chatsListFor(
|
||||
not_null<Dialogs::Entry*> entry) {
|
||||
const auto topic = entry->asTopic();
|
||||
return topic
|
||||
? topic->forum()->topicsList()
|
||||
: entry->asSublist()
|
||||
? _savedMessages->chatsList()
|
||||
: chatsList(entry->folder());
|
||||
if (const auto topic = entry->asTopic()) {
|
||||
return topic->forum()->topicsList();
|
||||
} else if (const auto sublist = entry->asSublist()) {
|
||||
return sublist->parent()->chatsList();
|
||||
}
|
||||
return chatsList(entry->folder());
|
||||
}
|
||||
|
||||
not_null<Dialogs::MainList*> Session::chatsList(Data::Folder *folder) {
|
||||
|
|
|
@ -781,11 +781,14 @@ void InnerWidget::changeOpenedForum(Data::Forum *forum) {
|
|||
}
|
||||
}
|
||||
|
||||
void InnerWidget::showSavedSublists() {
|
||||
void InnerWidget::showSavedSublists(ChannelData *parentChat) {
|
||||
Expects(!parentChat || parentChat->monoforum());
|
||||
Expects(!_geometryInited);
|
||||
Expects(!_savedSublists);
|
||||
|
||||
_savedSublists = true;
|
||||
_savedSublists = parentChat
|
||||
? parentChat->monoforum()
|
||||
: &session().data().savedMessages();
|
||||
|
||||
stopReorderPinned();
|
||||
clearSelection();
|
||||
|
@ -2115,7 +2118,7 @@ bool InnerWidget::addQuickActionRipple(
|
|||
const std::vector<Key> &InnerWidget::pinnedChatsOrder() const {
|
||||
const auto owner = &session().data();
|
||||
return _savedSublists
|
||||
? owner->pinnedChatsOrder(&owner->savedMessages())
|
||||
? owner->pinnedChatsOrder(_savedSublists)
|
||||
: _openedForum
|
||||
? owner->pinnedChatsOrder(_openedForum)
|
||||
: _filterId
|
||||
|
@ -2179,6 +2182,9 @@ int InnerWidget::countPinnedIndex(Row *ofRow) {
|
|||
}
|
||||
|
||||
void InnerWidget::savePinnedOrder() {
|
||||
if (_savedSublists && _savedSublists->parentChat()) {
|
||||
return;
|
||||
}
|
||||
const auto &newOrder = pinnedChatsOrder();
|
||||
if (newOrder.size() != _pinnedOnDragStart.size()) {
|
||||
return; // Something has changed in the set of pinned chats.
|
||||
|
@ -2316,8 +2322,11 @@ bool InnerWidget::updateReorderPinned(QPoint localPosition) {
|
|||
const auto delta = [&] {
|
||||
if (localPosition.y() < _visibleTop) {
|
||||
return localPosition.y() - _visibleTop;
|
||||
} else if ((_savedSublists || _openedFolder || _openedForum || _filterId)
|
||||
&& localPosition.y() > _visibleBottom) {
|
||||
} else if ((localPosition.y() > _visibleBottom)
|
||||
&& (_savedSublists
|
||||
|| _openedFolder
|
||||
|| _openedForum
|
||||
|| _filterId)) {
|
||||
return localPosition.y() - _visibleBottom;
|
||||
}
|
||||
return 0;
|
||||
|
@ -2685,8 +2694,8 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
|||
return false;
|
||||
} else if (const auto topic = event.key.topic()) {
|
||||
return (topic->forum() == _openedForum);
|
||||
} else if (event.key.sublist()) {
|
||||
return _savedSublists;
|
||||
} else if (const auto sublist = event.key.sublist()) {
|
||||
return sublist->parent() == _savedSublists;
|
||||
} else {
|
||||
return !_openedForum;
|
||||
}
|
||||
|
@ -2704,7 +2713,7 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
|||
&& (key.topic()
|
||||
? (key.topic()->forum() == _openedForum)
|
||||
: key.sublist()
|
||||
? _savedSublists
|
||||
? (key.sublist()->parent() == _savedSublists)
|
||||
: (entry->folder() == _openedFolder))) {
|
||||
_dialogMoved.fire({ from, to });
|
||||
}
|
||||
|
@ -2909,7 +2918,8 @@ void InnerWidget::enterEventHook(QEnterEvent *e) {
|
|||
Row *InnerWidget::shownRowByKey(Key key) {
|
||||
const auto entry = key.entry();
|
||||
if (_savedSublists) {
|
||||
if (!entry->asSublist()) {
|
||||
const auto sublist = entry->asSublist();
|
||||
if (!sublist || sublist->parent() != _savedSublists) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (_openedForum) {
|
||||
|
@ -2978,7 +2988,7 @@ void InnerWidget::updateSelectedRow(Key key) {
|
|||
|
||||
void InnerWidget::refreshShownList() {
|
||||
const auto list = _savedSublists
|
||||
? session().data().savedMessages().chatsList()->indexed()
|
||||
? _savedSublists->chatsList()->indexed()
|
||||
: _openedForum
|
||||
? _openedForum->topicsList()->indexed()
|
||||
: _filterId
|
||||
|
@ -3440,8 +3450,7 @@ void InnerWidget::applySearchState(SearchState state) {
|
|||
};
|
||||
if (_searchState.filterChatsList() && !words.isEmpty()) {
|
||||
if (_savedSublists) {
|
||||
const auto owner = &session().data();
|
||||
append(owner->savedMessages().chatsList()->indexed());
|
||||
append(_savedSublists->chatsList()->indexed());
|
||||
} else if (_openedForum) {
|
||||
append(_openedForum->topicsList()->indexed());
|
||||
} else {
|
||||
|
@ -4012,7 +4021,7 @@ void InnerWidget::refreshEmpty() {
|
|||
const auto state = !_shownList->empty()
|
||||
? EmptyState::None
|
||||
: _savedSublists
|
||||
? (data->savedMessages().chatsList()->loaded()
|
||||
? (_savedSublists->chatsList()->loaded()
|
||||
? EmptyState::EmptySavedSublists
|
||||
: EmptyState::Loading)
|
||||
: _openedForum
|
||||
|
|
|
@ -58,6 +58,7 @@ class ChatFilter;
|
|||
class Thread;
|
||||
class Folder;
|
||||
class Forum;
|
||||
class SavedMessages;
|
||||
struct ReactionId;
|
||||
} // namespace Data
|
||||
|
||||
|
@ -140,7 +141,7 @@ public:
|
|||
|
||||
void changeOpenedFolder(Data::Folder *folder);
|
||||
void changeOpenedForum(Data::Forum *forum);
|
||||
void showSavedSublists();
|
||||
void showSavedSublists(ChannelData *parentChat);
|
||||
void selectSkip(int32 direction);
|
||||
void selectSkipPage(int32 pixels, int32 direction);
|
||||
|
||||
|
@ -668,7 +669,8 @@ private:
|
|||
float64 _narrowRatio = 0.;
|
||||
bool _geometryInited = false;
|
||||
|
||||
bool _savedSublists = false;
|
||||
Data::SavedMessages *_savedSublists = nullptr;
|
||||
|
||||
bool _searchLoading = false;
|
||||
bool _searchWaiting = false;
|
||||
|
||||
|
|
|
@ -1712,6 +1712,7 @@ ServiceAction ParseServiceAction(
|
|||
}, [&](const MTPDmessageActionPaidMessagesPrice &data) {
|
||||
result.content = ActionPaidMessagesPrice{
|
||||
.stars = int(data.vstars().v),
|
||||
.broadcastAllowed = data.is_broadcast_messages_allowed(),
|
||||
};
|
||||
}, [&](const MTPDmessageActionConferenceCall &data) {
|
||||
auto content = ActionPhoneCall();
|
||||
|
|
|
@ -673,6 +673,7 @@ struct ActionPaidMessagesRefunded {
|
|||
|
||||
struct ActionPaidMessagesPrice {
|
||||
int stars = 0;
|
||||
bool broadcastAllowed = false;
|
||||
};
|
||||
|
||||
struct ServiceAction {
|
||||
|
|
|
@ -1383,7 +1383,15 @@ auto HtmlWriter::Wrap::pushMessage(
|
|||
+ " messages to you");
|
||||
return result;
|
||||
}, [&](const ActionPaidMessagesPrice &data) {
|
||||
auto result = "Price per messages changed to "
|
||||
if (isChannel) {
|
||||
auto result = !data.broadcastAllowed
|
||||
? "Direct messages were disabled."
|
||||
: ("Price per direct message changed to "
|
||||
+ QString::number(data.stars).toUtf8()
|
||||
+ " Telegram Stars.");
|
||||
return result;
|
||||
}
|
||||
auto result = "Price per message changed to "
|
||||
+ QString::number(data.stars).toUtf8()
|
||||
+ " Telegram Stars.";
|
||||
return result;
|
||||
|
|
|
@ -679,6 +679,7 @@ QByteArray SerializeMessage(
|
|||
pushActor();
|
||||
pushAction("paid_messages_price_change");
|
||||
push("price_stars", data.stars);
|
||||
push("is_broadcast_messages_allowed", data.broadcastAllowed);
|
||||
}, [](v::null_t) {});
|
||||
|
||||
if (v::is_null(message.action.content)) {
|
||||
|
|
|
@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
@ -3131,6 +3132,42 @@ bool History::isForum() const {
|
|||
return (_flags & Flag::IsForum);
|
||||
}
|
||||
|
||||
void History::monoforumChanged(Data::SavedMessages *old) {
|
||||
if (inChatList()) {
|
||||
notifyUnreadStateChange(old
|
||||
? AdjustedForumUnreadState(old->chatsList()->unreadState())
|
||||
: computeUnreadState());
|
||||
}
|
||||
|
||||
if (const auto monoforum = peer->monoforum()) {
|
||||
_flags |= Flag::IsMonoforum;
|
||||
|
||||
monoforum->chatsList()->unreadStateChanges(
|
||||
) | rpl::filter([=] {
|
||||
return (_flags & Flag::IsMonoforum) && inChatList();
|
||||
}) | rpl::map(
|
||||
AdjustedForumUnreadState
|
||||
) | rpl::start_with_next([=](const Dialogs::UnreadState &old) {
|
||||
notifyUnreadStateChange(old);
|
||||
}, monoforum->lifetime());
|
||||
|
||||
monoforum->chatsListChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateChatListEntry();
|
||||
}, monoforum->lifetime());
|
||||
} else {
|
||||
_flags &= ~Flag::IsMonoforum;
|
||||
}
|
||||
if (cloudDraft(MsgId(0))) {
|
||||
updateChatListSortPosition();
|
||||
}
|
||||
_flags |= Flag::PendingAllItemsResize;
|
||||
}
|
||||
|
||||
bool History::isMonoforum() const {
|
||||
return (_flags & Flag::IsMonoforum);
|
||||
}
|
||||
|
||||
not_null<History*> History::migrateToOrMe() const {
|
||||
if (const auto to = peer->migrateTo()) {
|
||||
return owner().history(to);
|
||||
|
|
|
@ -27,12 +27,14 @@ struct LanguageId;
|
|||
|
||||
namespace Data {
|
||||
struct Draft;
|
||||
class Forum;
|
||||
class Session;
|
||||
class Folder;
|
||||
class ChatFilter;
|
||||
struct SponsoredFrom;
|
||||
class SponsoredMessages;
|
||||
class HistoryMessages;
|
||||
class SavedMessages;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -71,6 +73,9 @@ public:
|
|||
void forumChanged(Data::Forum *old);
|
||||
[[nodiscard]] bool isForum() const;
|
||||
|
||||
void monoforumChanged(Data::SavedMessages *old);
|
||||
[[nodiscard]] bool isMonoforum() const;
|
||||
|
||||
[[nodiscard]] not_null<History*> migrateToOrMe() const;
|
||||
[[nodiscard]] History *migrateFrom() const;
|
||||
[[nodiscard]] MsgRange rangeForDifferenceRequest() const;
|
||||
|
@ -430,9 +435,10 @@ private:
|
|||
PendingAllItemsResize = (1 << 1),
|
||||
IsTopPromoted = (1 << 2),
|
||||
IsForum = (1 << 3),
|
||||
FakeUnreadWhileOpened = (1 << 4),
|
||||
HasPinnedMessages = (1 << 5),
|
||||
ResolveChatListMessage = (1 << 6),
|
||||
IsMonoforum = (1 << 4),
|
||||
FakeUnreadWhileOpened = (1 << 5),
|
||||
HasPinnedMessages = (1 << 6),
|
||||
ResolveChatListMessage = (1 << 7),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) {
|
||||
|
|
|
@ -3563,6 +3563,12 @@ Data::SavedSublist *HistoryItem::savedSublist() const {
|
|||
that->AddComponents(HistoryMessageSaved::Bit());
|
||||
that->Get<HistoryMessageSaved>()->sublist = sublist;
|
||||
return sublist;
|
||||
} else if (const auto monoforum = _history->peer->monoforum()) {
|
||||
const auto sublist = monoforum->sublist(_history->peer);
|
||||
const auto that = const_cast<HistoryItem*>(this);
|
||||
that->AddComponents(HistoryMessageSaved::Bit());
|
||||
that->Get<HistoryMessageSaved>()->sublist = sublist;
|
||||
return sublist;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3785,7 +3791,9 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
|||
}
|
||||
}
|
||||
const auto peer = _history->owner().peer(config.savedSublistPeer);
|
||||
saved->sublist = _history->owner().savedMessages().sublist(peer);
|
||||
saved->sublist = _history->peer->isSelf()
|
||||
? _history->owner().savedMessages().sublist(peer)
|
||||
: _history->peer->monoforum()->sublist(peer);
|
||||
}
|
||||
|
||||
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||
|
@ -5744,8 +5752,23 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
|
||||
auto preparePaidMessagesPrice = [&](const MTPDmessageActionPaidMessagesPrice &action) {
|
||||
const auto stars = action.vstars().v;
|
||||
const auto broadcastAllowed = action.is_broadcast_messages_allowed();
|
||||
auto result = PreparedServiceText();
|
||||
result.text = stars
|
||||
result.text = _history->peer->isBroadcast()
|
||||
? (stars > 0
|
||||
? tr::lng_action_direct_messages_paid(
|
||||
tr::now,
|
||||
lt_count,
|
||||
stars,
|
||||
Ui::Text::WithEntities)
|
||||
: broadcastAllowed
|
||||
? tr::lng_action_direct_messages_enabled(
|
||||
tr::now,
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_action_direct_messages_disabled(
|
||||
tr::now,
|
||||
Ui::Text::WithEntities))
|
||||
: stars
|
||||
? tr::lng_action_message_price_paid(
|
||||
tr::now,
|
||||
lt_count,
|
||||
|
|
|
@ -383,6 +383,7 @@ HistoryWidget::HistoryWidget(
|
|||
_joinChannel->addClickHandler([=] { joinChannel(); });
|
||||
_muteUnmute->addClickHandler([=] { toggleMuteUnmute(); });
|
||||
setupGiftToChannelButton();
|
||||
setupDirectMessageButton();
|
||||
_reportMessages->addClickHandler([=] { reportSelectedMessages(); });
|
||||
_field->submits(
|
||||
) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) {
|
||||
|
@ -1050,15 +1051,23 @@ void HistoryWidget::refreshJoinChannelText() {
|
|||
}
|
||||
|
||||
void HistoryWidget::refreshGiftToChannelShown() {
|
||||
if (!_giftToChannelIn || !_giftToChannelOut) {
|
||||
if (!_giftToChannel || !_peer) {
|
||||
return;
|
||||
}
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto shown = channel
|
||||
_giftToChannel->setVisible(channel
|
||||
&& channel->isBroadcast()
|
||||
&& channel->stargiftsAvailable();
|
||||
_giftToChannelIn->setVisible(shown);
|
||||
_giftToChannelOut->setVisible(shown);
|
||||
&& channel->stargiftsAvailable());
|
||||
}
|
||||
|
||||
void HistoryWidget::refreshDirectMessageShown() {
|
||||
if (!_directMessage || !_peer) {
|
||||
return;
|
||||
}
|
||||
const auto channel = _peer->asChannel();
|
||||
_directMessage->setVisible(channel
|
||||
&& channel->isBroadcast()
|
||||
&& channel->monoforumLink());
|
||||
}
|
||||
|
||||
void HistoryWidget::refreshTopBarActiveChat() {
|
||||
|
@ -2074,22 +2083,63 @@ void HistoryWidget::setupShortcuts() {
|
|||
}
|
||||
|
||||
void HistoryWidget::setupGiftToChannelButton() {
|
||||
const auto setupButton = [=](not_null<Ui::RpWidget*> parent) {
|
||||
auto *button = Ui::CreateChild<Ui::IconButton>(
|
||||
parent.get(),
|
||||
st::historyGiftToChannel);
|
||||
parent->widthValue() | rpl::start_with_next([=](int width) {
|
||||
button->moveToRight(0, 0);
|
||||
}, button->lifetime());
|
||||
button->setClickedCallback([=] {
|
||||
if (_peer) {
|
||||
Ui::ShowStarGiftBox(controller(), _peer);
|
||||
_giftToChannel = Ui::CreateChild<Ui::IconButton>(
|
||||
_muteUnmute.data(),
|
||||
st::historyGiftToChannel);
|
||||
widthValue() | rpl::start_with_next([=](int width) {
|
||||
_giftToChannel->moveToRight(0, 0, width);
|
||||
}, _giftToChannel->lifetime());
|
||||
_giftToChannel->setClickedCallback([=] {
|
||||
Ui::ShowStarGiftBox(controller(), _peer);
|
||||
});
|
||||
rpl::combine(
|
||||
_muteUnmute->shownValue(),
|
||||
_joinChannel->shownValue()
|
||||
) | rpl::start_with_next([=](bool muteUnmute, bool joinChannel) {
|
||||
const auto newParent = (muteUnmute && !joinChannel)
|
||||
? _muteUnmute.data()
|
||||
: (joinChannel && !muteUnmute)
|
||||
? _joinChannel.data()
|
||||
: nullptr;
|
||||
if (newParent) {
|
||||
_giftToChannel->setParent(newParent);
|
||||
_giftToChannel->moveToRight(0, 0);
|
||||
refreshGiftToChannelShown();
|
||||
}
|
||||
}, _giftToChannel->lifetime());
|
||||
}
|
||||
|
||||
void HistoryWidget::setupDirectMessageButton() {
|
||||
_directMessage = Ui::CreateChild<Ui::IconButton>(
|
||||
_muteUnmute.data(),
|
||||
st::historyDirectMessage);
|
||||
widthValue() | rpl::start_with_next([=](int width) {
|
||||
_directMessage->moveToRight(0, 0, width);
|
||||
}, _directMessage->lifetime());
|
||||
_directMessage->setClickedCallback([=] {
|
||||
if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||
if (const auto monoforum = channel->monoforumLink()) {
|
||||
controller()->showPeerHistory(
|
||||
monoforum,
|
||||
Window::SectionShow::Way::Forward);
|
||||
}
|
||||
});
|
||||
return button;
|
||||
};
|
||||
_giftToChannelIn = setupButton(_muteUnmute);
|
||||
_giftToChannelOut = setupButton(_joinChannel);
|
||||
}
|
||||
});
|
||||
rpl::combine(
|
||||
_muteUnmute->shownValue(),
|
||||
_joinChannel->shownValue()
|
||||
) | rpl::start_with_next([=](bool muteUnmute, bool joinChannel) {
|
||||
const auto newParent = (muteUnmute && !joinChannel)
|
||||
? _muteUnmute.data()
|
||||
: (joinChannel && !muteUnmute)
|
||||
? _joinChannel.data()
|
||||
: nullptr;
|
||||
if (newParent) {
|
||||
_directMessage->setParent(newParent);
|
||||
_directMessage->moveToLeft(0, 0);
|
||||
refreshDirectMessageShown();
|
||||
}
|
||||
}, _directMessage->lifetime());
|
||||
}
|
||||
|
||||
void HistoryWidget::pushReplyReturn(not_null<HistoryItem*> item) {
|
||||
|
@ -2456,6 +2506,7 @@ void HistoryWidget::showHistory(
|
|||
}, _contactStatus->bar().lifetime());
|
||||
|
||||
refreshGiftToChannelShown();
|
||||
refreshDirectMessageShown();
|
||||
if (const auto user = _peer->asUser()) {
|
||||
_paysStatus = std::make_unique<PaysStatus>(
|
||||
controller(),
|
||||
|
@ -5220,7 +5271,10 @@ bool HistoryWidget::isBlocked() const {
|
|||
}
|
||||
|
||||
bool HistoryWidget::isJoinChannel() const {
|
||||
return _peer && _peer->isChannel() && !_peer->asChannel()->amIn();
|
||||
if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||
return !channel->amIn() && !channel->isMonoforum();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryWidget::isChoosingTheme() const {
|
||||
|
@ -8639,6 +8693,7 @@ void HistoryWidget::fullInfoUpdated() {
|
|||
sendBotStartCommand();
|
||||
}
|
||||
refreshGiftToChannelShown();
|
||||
refreshDirectMessageShown();
|
||||
}
|
||||
if (updateCmdStartShown()) {
|
||||
refresh = true;
|
||||
|
|
|
@ -406,6 +406,7 @@ private:
|
|||
|
||||
void refreshJoinChannelText();
|
||||
void refreshGiftToChannelShown();
|
||||
void refreshDirectMessageShown();
|
||||
void requestMessageData(MsgId msgId);
|
||||
void messageDataReceived(not_null<PeerData*> peer, MsgId msgId);
|
||||
|
||||
|
@ -535,6 +536,7 @@ private:
|
|||
|
||||
void setupShortcuts();
|
||||
void setupGiftToChannelButton();
|
||||
void setupDirectMessageButton();
|
||||
|
||||
void handlePeerMigration();
|
||||
|
||||
|
@ -797,8 +799,8 @@ private:
|
|||
object_ptr<Ui::FlatButton> _botStart;
|
||||
object_ptr<Ui::FlatButton> _joinChannel;
|
||||
object_ptr<Ui::FlatButton> _muteUnmute;
|
||||
QPointer<Ui::IconButton> _giftToChannelIn;
|
||||
QPointer<Ui::IconButton> _giftToChannelOut;
|
||||
QPointer<Ui::IconButton> _giftToChannel;
|
||||
QPointer<Ui::IconButton> _directMessage;
|
||||
object_ptr<Ui::FlatButton> _reportMessages;
|
||||
struct {
|
||||
object_ptr<Ui::RoundButton> button = { nullptr };
|
||||
|
|
|
@ -606,7 +606,7 @@ rpl::producer<Data::MessagesSlice> SublistWidget::listSource(
|
|||
? (*result.fullCount - after - useBefore)
|
||||
: std::optional<int>();
|
||||
if (!result.fullCount || useBefore < limitBefore) {
|
||||
_sublist->owner().savedMessages().loadMore(_sublist);
|
||||
_sublist->parent()->loadMore(_sublist);
|
||||
}
|
||||
consumer.put_next(std::move(result));
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_stories_ids.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/view/history_view_sublist_section.h"
|
||||
#include "history/history.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
|
@ -32,39 +33,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Info::Media {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] Window::SeparateSharedMediaType ToSeparateType(
|
||||
Storage::SharedMediaType type) {
|
||||
[[nodiscard]] bool SeparateSupported(Storage::SharedMediaType type) {
|
||||
using Type = Storage::SharedMediaType;
|
||||
using SeparatedType = Window::SeparateSharedMediaType;
|
||||
return (type == Type::Photo)
|
||||
? SeparatedType::Photos
|
||||
: (type == Type::Video)
|
||||
? SeparatedType::Videos
|
||||
: (type == Type::File)
|
||||
? SeparatedType::Files
|
||||
: (type == Type::MusicFile)
|
||||
? SeparatedType::Audio
|
||||
: (type == Type::Link)
|
||||
? SeparatedType::Links
|
||||
: (type == Type::RoundVoiceFile)
|
||||
? SeparatedType::Voices
|
||||
: (type == Type::GIF)
|
||||
? SeparatedType::GIF
|
||||
: SeparatedType::None;
|
||||
|| (type == Type::Video)
|
||||
|| (type == Type::File)
|
||||
|| (type == Type::MusicFile)
|
||||
|| (type == Type::Link)
|
||||
|| (type == Type::RoundVoiceFile)
|
||||
|| (type == Type::GIF);
|
||||
}
|
||||
|
||||
[[nodiscard]] Window::SeparateId SeparateId(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId topicRootId,
|
||||
Storage::SharedMediaType type) {
|
||||
if (peer->isSelf()) {
|
||||
if (peer->isSelf() || !SeparateSupported(type)) {
|
||||
return { nullptr };
|
||||
}
|
||||
const auto separateType = ToSeparateType(type);
|
||||
if (separateType == Window::SeparateSharedMediaType::None) {
|
||||
const auto topic = topicRootId
|
||||
? peer->forumTopicFor(topicRootId)
|
||||
: nullptr;
|
||||
if (topicRootId && !topic) {
|
||||
return { nullptr };
|
||||
}
|
||||
return { Window::SeparateSharedMedia{ separateType, peer, topicRootId } };
|
||||
const auto thread = topic
|
||||
? (Data::Thread*)topic
|
||||
: peer->owner().history(peer);
|
||||
return { thread, type };
|
||||
}
|
||||
|
||||
void AddContextMenuToButton(
|
||||
|
|
|
@ -40,6 +40,28 @@ Type TabIndexToType(int index) {
|
|||
Unexpected("Index in Info::Media::TabIndexToType()");
|
||||
}
|
||||
|
||||
tr::phrase<> SharedMediaTitle(Type type) {
|
||||
switch (type) {
|
||||
case Type::Photo:
|
||||
return tr::lng_media_type_photos;
|
||||
case Type::GIF:
|
||||
return tr::lng_media_type_gifs;
|
||||
case Type::Video:
|
||||
return tr::lng_media_type_videos;
|
||||
case Type::MusicFile:
|
||||
return tr::lng_media_type_songs;
|
||||
case Type::File:
|
||||
return tr::lng_media_type_files;
|
||||
case Type::RoundVoiceFile:
|
||||
return tr::lng_media_type_audios;
|
||||
case Type::Link:
|
||||
return tr::lng_media_type_links;
|
||||
case Type::RoundFile:
|
||||
return tr::lng_media_type_rounds;
|
||||
}
|
||||
Unexpected("Bad media type in Info::TitleValue()");
|
||||
}
|
||||
|
||||
Memento::Memento(not_null<Controller*> controller)
|
||||
: Memento(
|
||||
(controller->peer()
|
||||
|
@ -119,25 +141,7 @@ rpl::producer<QString> Widget::title() {
|
|||
if (controller()->key().peer()->sharedMediaInfo() && isStackBottom()) {
|
||||
return tr::lng_profile_shared_media();
|
||||
}
|
||||
switch (controller()->section().mediaType()) {
|
||||
case Section::MediaType::Photo:
|
||||
return tr::lng_media_type_photos();
|
||||
case Section::MediaType::GIF:
|
||||
return tr::lng_media_type_gifs();
|
||||
case Section::MediaType::Video:
|
||||
return tr::lng_media_type_videos();
|
||||
case Section::MediaType::MusicFile:
|
||||
return tr::lng_media_type_songs();
|
||||
case Section::MediaType::File:
|
||||
return tr::lng_media_type_files();
|
||||
case Section::MediaType::RoundVoiceFile:
|
||||
return tr::lng_media_type_audios();
|
||||
case Section::MediaType::Link:
|
||||
return tr::lng_media_type_links();
|
||||
case Section::MediaType::RoundFile:
|
||||
return tr::lng_media_type_rounds();
|
||||
}
|
||||
Unexpected("Bad media type in Info::TitleValue()");
|
||||
return SharedMediaTitle(controller()->section().mediaType())();
|
||||
}
|
||||
|
||||
void Widget::setIsStackBottom(bool isStackBottom) {
|
||||
|
|
|
@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/storage_shared_media.h"
|
||||
#include "data/data_search_controller.h"
|
||||
|
||||
namespace tr {
|
||||
template <typename ...Tags>
|
||||
struct phrase;
|
||||
} // namespace tr
|
||||
|
||||
namespace Data {
|
||||
class ForumTopic;
|
||||
} // namespace Data
|
||||
|
@ -19,8 +24,9 @@ namespace Info::Media {
|
|||
|
||||
using Type = Storage::SharedMediaType;
|
||||
|
||||
std::optional<int> TypeToTabIndex(Type type);
|
||||
Type TabIndexToType(int index);
|
||||
[[nodiscard]] std::optional<int> TypeToTabIndex(Type type);
|
||||
[[nodiscard]] Type TabIndexToType(int index);
|
||||
[[nodiscard]] tr::phrase<> SharedMediaTitle(Type type);
|
||||
|
||||
class InnerWidget;
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ SublistsWidget::SublistsWidget(
|
|||
this,
|
||||
controller->parentController(),
|
||||
rpl::single(Dialogs::InnerWidget::ChildListShown())));
|
||||
_list->showSavedSublists();
|
||||
_list->showSavedSublists(nullptr);
|
||||
_list->setNarrowRatio(0.);
|
||||
|
||||
_list->chosenRow() | rpl::start_with_next([=](Dialogs::ChosenRow row) {
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/platform/ui_platform_window.h"
|
||||
#include "platform/platform_window_title.h"
|
||||
#include "history/history.h"
|
||||
#include "info/media/info_media_widget.h" // SharedMediaTitle.
|
||||
#include "window/window_separate_id.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_lock_widgets.h"
|
||||
|
@ -87,42 +88,24 @@ base::options::toggle OptionDisableTouchbar({
|
|||
.restartRequired = true,
|
||||
});
|
||||
|
||||
[[nodiscard]] QString TitleFromSeparateId(
|
||||
[[nodiscard]] QString TitleFromSeparateSharedMedia(
|
||||
const Core::WindowTitleContent &settings,
|
||||
const SeparateId &id) {
|
||||
if (id.sharedMedia == SeparateSharedMediaType::None
|
||||
|| !id.sharedMediaPeer()) {
|
||||
if (id.type != SeparateType::SharedMedia) {
|
||||
return QString();
|
||||
}
|
||||
const auto result = (id.sharedMedia == SeparateSharedMediaType::Photos)
|
||||
? tr::lng_media_type_photos(tr::now)
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Videos)
|
||||
? tr::lng_media_type_videos(tr::now)
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Files)
|
||||
? tr::lng_media_type_files(tr::now)
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Audio)
|
||||
? tr::lng_media_type_songs(tr::now)
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Links)
|
||||
? tr::lng_media_type_links(tr::now)
|
||||
: (id.sharedMedia == SeparateSharedMediaType::GIF)
|
||||
? tr::lng_media_type_gifs(tr::now)
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Voices)
|
||||
? tr::lng_media_type_audios(tr::now)
|
||||
: QString();
|
||||
|
||||
const auto type = id.sharedMediaType;
|
||||
const auto result = Info::Media::SharedMediaTitle(type)(tr::now);
|
||||
if (settings.hideChatName) {
|
||||
return result;
|
||||
}
|
||||
const auto peer = id.sharedMediaPeer();
|
||||
const auto topicRootId = id.sharedMediaTopicRootId();
|
||||
const auto topic = topicRootId
|
||||
? peer->forumTopicFor(topicRootId)
|
||||
: nullptr;
|
||||
const auto thread = id.thread;
|
||||
const auto topic = thread->asTopic();
|
||||
const auto name = topic
|
||||
? topic->title()
|
||||
: peer->isSelf()
|
||||
: thread->peer()->isSelf()
|
||||
? tr::lng_saved_messages(tr::now)
|
||||
: peer->name();
|
||||
: thread->peer()->name();
|
||||
const auto wrapped = st::wrap_rtl(name);
|
||||
return name + u" @ "_q + result;
|
||||
}
|
||||
|
@ -902,11 +885,11 @@ void MainWindow::updateTitle() {
|
|||
&& Core::App().domain().accountsAuthedCount() > 1)
|
||||
? st::wrap_rtl(session->authedName())
|
||||
: QString();
|
||||
const auto separateIdTitle = session
|
||||
? TitleFromSeparateId(settings, session->windowId())
|
||||
const auto separateSharedMediaTitle = session
|
||||
? TitleFromSeparateSharedMedia(settings, session->windowId())
|
||||
: QString();
|
||||
if (!separateIdTitle.isEmpty()) {
|
||||
setTitle(separateIdTitle);
|
||||
if (!separateSharedMediaTitle.isEmpty()) {
|
||||
setTitle(separateSharedMediaTitle);
|
||||
return;
|
||||
}
|
||||
const auto key = (session && !settings.hideChatName)
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "window/window_separate_id.h"
|
||||
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
|
@ -30,10 +31,14 @@ SeparateId::SeparateId(SeparateType type, not_null<Main::Session*> session)
|
|||
, account(&session->account()) {
|
||||
}
|
||||
|
||||
SeparateId::SeparateId(SeparateType type, not_null<Data::Thread*> thread)
|
||||
SeparateId::SeparateId(
|
||||
SeparateType type,
|
||||
not_null<Data::Thread*> thread,
|
||||
ChannelData *parentChat)
|
||||
: type(type)
|
||||
, account(&thread->session().account())
|
||||
, thread(thread) {
|
||||
, thread(thread)
|
||||
, parentChat((type == SeparateType::SavedSublist) ? parentChat : nullptr) {
|
||||
}
|
||||
|
||||
SeparateId::SeparateId(not_null<Data::Thread*> thread)
|
||||
|
@ -44,12 +49,13 @@ SeparateId::SeparateId(not_null<PeerData*> peer)
|
|||
: SeparateId(SeparateType::Chat, peer->owner().history(peer)) {
|
||||
}
|
||||
|
||||
SeparateId::SeparateId(SeparateSharedMedia data)
|
||||
SeparateId::SeparateId(
|
||||
not_null<Data::Thread*> thread,
|
||||
Storage::SharedMediaType sharedMediaType)
|
||||
: type(SeparateType::SharedMedia)
|
||||
, sharedMedia(data.type)
|
||||
, account(&data.peer->session().account())
|
||||
, sharedMediaDataPeer(data.peer)
|
||||
, sharedMediaDataTopicRootId(data.topicRootId) {
|
||||
, sharedMediaType(sharedMediaType)
|
||||
, account(&thread->session().account())
|
||||
, thread(thread) {
|
||||
}
|
||||
|
||||
bool SeparateId::primary() const {
|
||||
|
@ -71,9 +77,12 @@ Data::Folder *SeparateId::folder() const {
|
|||
}
|
||||
|
||||
Data::SavedSublist *SeparateId::sublist() const {
|
||||
return (type == SeparateType::SavedSublist)
|
||||
? thread->owner().savedMessages().sublist(thread->peer()).get()
|
||||
: nullptr;
|
||||
const auto monoforum = parentChat ? parentChat->monoforum() : nullptr;
|
||||
return (type != SeparateType::SavedSublist)
|
||||
? nullptr
|
||||
: monoforum
|
||||
? monoforum->sublist(thread->peer()).get()
|
||||
: thread->owner().savedMessages().sublist(thread->peer()).get();
|
||||
}
|
||||
|
||||
bool SeparateId::hasChatsList() const {
|
||||
|
@ -82,16 +91,4 @@ bool SeparateId::hasChatsList() const {
|
|||
|| (type == SeparateType::Forum);
|
||||
}
|
||||
|
||||
PeerData *SeparateId::sharedMediaPeer() const {
|
||||
return (type == SeparateType::SharedMedia)
|
||||
? sharedMediaDataPeer
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
MsgId SeparateId::sharedMediaTopicRootId() const {
|
||||
return (type == SeparateType::SharedMedia)
|
||||
? sharedMediaDataTopicRootId
|
||||
: MsgId();
|
||||
}
|
||||
|
||||
} // namespace Window
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
class ChannelData;
|
||||
class PeerData;
|
||||
|
||||
namespace Data {
|
||||
|
@ -21,6 +22,10 @@ class Account;
|
|||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Storage {
|
||||
enum class SharedMediaType : signed char;
|
||||
} // namespace Storage
|
||||
|
||||
namespace Window {
|
||||
|
||||
enum class SeparateType {
|
||||
|
@ -32,39 +37,30 @@ enum class SeparateType {
|
|||
SharedMedia,
|
||||
};
|
||||
|
||||
enum class SeparateSharedMediaType {
|
||||
None,
|
||||
Photos,
|
||||
Videos,
|
||||
Files,
|
||||
Audio,
|
||||
Links,
|
||||
Voices,
|
||||
GIF,
|
||||
};
|
||||
|
||||
struct SeparateSharedMedia {
|
||||
SeparateSharedMediaType type = SeparateSharedMediaType::None;
|
||||
not_null<PeerData*> peer;
|
||||
MsgId topicRootId = MsgId();
|
||||
not_null<Data::Thread*> thread;
|
||||
Storage::SharedMediaType type = {};
|
||||
};
|
||||
|
||||
struct SeparateId {
|
||||
SeparateId(std::nullptr_t);
|
||||
SeparateId(not_null<Main::Account*> account);
|
||||
SeparateId(SeparateType type, not_null<Main::Session*> session);
|
||||
SeparateId(SeparateType type, not_null<Data::Thread*> thread);
|
||||
SeparateId(
|
||||
SeparateType type,
|
||||
not_null<Data::Thread*> thread,
|
||||
ChannelData *parentChat = nullptr);
|
||||
SeparateId(not_null<Data::Thread*> thread);
|
||||
SeparateId(not_null<PeerData*> peer);
|
||||
SeparateId(SeparateSharedMedia data);
|
||||
SeparateId(
|
||||
not_null<Data::Thread*> thread,
|
||||
Storage::SharedMediaType sharedMediaType);
|
||||
|
||||
SeparateType type = SeparateType::Primary;
|
||||
SeparateSharedMediaType sharedMedia = SeparateSharedMediaType::None;
|
||||
Storage::SharedMediaType sharedMediaType = {};
|
||||
Main::Account *account = nullptr;
|
||||
Data::Thread *thread = nullptr; // For types except Main and Archive.
|
||||
PeerData *sharedMediaDataPeer = nullptr;
|
||||
MsgId sharedMediaDataTopicRootId = MsgId();
|
||||
|
||||
ChannelData *parentChat = nullptr;
|
||||
[[nodiscard]] bool valid() const {
|
||||
return account != nullptr;
|
||||
}
|
||||
|
@ -77,8 +73,6 @@ struct SeparateId {
|
|||
[[nodiscard]] Data::Forum *forum() const;
|
||||
[[nodiscard]] Data::Folder *folder() const;
|
||||
[[nodiscard]] Data::SavedSublist *sublist() const;
|
||||
[[nodiscard]] PeerData *sharedMediaPeer() const;
|
||||
[[nodiscard]] MsgId sharedMediaTopicRootId() const;
|
||||
|
||||
[[nodiscard]] bool hasChatsList() const;
|
||||
|
||||
|
|
|
@ -1321,35 +1321,13 @@ void SessionNavigation::showByInitialId(
|
|||
showThread(id.thread, msgId, instant);
|
||||
break;
|
||||
case SeparateType::SharedMedia: {
|
||||
Assert(id.sharedMedia != SeparateSharedMediaType::None);
|
||||
clearSectionStack(instant);
|
||||
const auto type = (id.sharedMedia == SeparateSharedMediaType::Photos)
|
||||
? Storage::SharedMediaType::Photo
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Videos)
|
||||
? Storage::SharedMediaType::Video
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Files)
|
||||
? Storage::SharedMediaType::File
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Audio)
|
||||
? Storage::SharedMediaType::MusicFile
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Links)
|
||||
? Storage::SharedMediaType::Link
|
||||
: (id.sharedMedia == SeparateSharedMediaType::Voices)
|
||||
? Storage::SharedMediaType::RoundVoiceFile
|
||||
: (id.sharedMedia == SeparateSharedMediaType::GIF)
|
||||
? Storage::SharedMediaType::GIF
|
||||
: Storage::SharedMediaType::Photo;
|
||||
const auto topicRootId = id.sharedMediaTopicRootId();
|
||||
const auto peer = id.sharedMediaPeer();
|
||||
const auto topic = topicRootId
|
||||
? peer->forumTopicFor(topicRootId)
|
||||
: nullptr;
|
||||
if (topicRootId && !topic) {
|
||||
break;
|
||||
}
|
||||
const auto type = id.sharedMediaType;
|
||||
const auto topic = id.thread->asTopic();
|
||||
showSection(
|
||||
topicRootId
|
||||
(topic
|
||||
? std::make_shared<Info::Memento>(topic, type)
|
||||
: std::make_shared<Info::Memento>(peer, type),
|
||||
: std::make_shared<Info::Memento>(id.thread->peer(), type)),
|
||||
instant);
|
||||
parent->widget()->setMaximumWidth(st::maxWidthSharedMediaWindow);
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue