mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-03 21:54:05 +02:00
Initial frozen accounts support.
This commit is contained in:
parent
7f53a19647
commit
d9b270b477
29 changed files with 481 additions and 39 deletions
1
Telegram/Resources/animations/media_forbidden.tgs
Normal file
1
Telegram/Resources/animations/media_forbidden.tgs
Normal file
File diff suppressed because one or more lines are too long
BIN
Telegram/Resources/icons/menu/hourglass.png
Normal file
BIN
Telegram/Resources/icons/menu/hourglass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 630 B |
BIN
Telegram/Resources/icons/menu/hourglass@2x.png
Normal file
BIN
Telegram/Resources/icons/menu/hourglass@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/menu/hourglass@3x.png
Normal file
BIN
Telegram/Resources/icons/menu/hourglass@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -6268,6 +6268,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_qr_box_transparent_background" = "Transparent Background";
|
||||
"lng_qr_box_font_size" = "Font size";
|
||||
|
||||
"lng_frozen_bar_title" = "Your account is frozen!";
|
||||
"lng_frozen_bar_text" = "Click to view details {arrow}";
|
||||
"lng_frozen_restrict_title" = "Your account is frozen";
|
||||
"lng_frozen_restrict_text" = "Click to view details";
|
||||
"lng_frozen_title" = "Your Account is Frozen";
|
||||
"lng_frozen_subtitle1" = "Violation of Terms";
|
||||
"lng_frozen_text1" = "Your account was frozen for breaking Telegram's Terms and Conditions.";
|
||||
"lng_frozen_subtitle2" = "Read-Only Mode";
|
||||
"lng_frozen_text2" = "You can access your account but can't send messages or take actions.";
|
||||
"lng_frozen_subtitle3" = "Appeal Before Deactivation";
|
||||
"lng_frozen_text3" = "Appeal via {link} before {date}, or your account will be deleted.";
|
||||
"lng_frozen_appeal_button" = "Submit an Appeal";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<file alias="noresults.tgs">../../animations/noresults.tgs</file>
|
||||
<file alias="hello_status.tgs">../../animations/hello_status.tgs</file>
|
||||
<file alias="starref_link.tgs">../../animations/starref_link.tgs</file>
|
||||
<file alias="media_forbidden.tgs">../../animations/media_forbidden.tgs</file>
|
||||
|
||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||
|
|
|
@ -1566,3 +1566,27 @@ processingVideoView: RoundButton(defaultActiveButton) {
|
|||
textBgOver: transparent;
|
||||
ripple: emptyRippleAnimation;
|
||||
}
|
||||
|
||||
frozenBarTitle: FlatLabel(defaultFlatLabel) {
|
||||
style: semiboldTextStyle;
|
||||
textFg: attentionButtonFg;
|
||||
}
|
||||
frozenRestrictionTitle: FlatLabel(frozenBarTitle) {
|
||||
align: align(top);
|
||||
}
|
||||
frozenBarSubtitle: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
frozenRestrictionSubtitle: FlatLabel(frozenBarSubtitle) {
|
||||
align: align(top);
|
||||
}
|
||||
frozenInfoBox: Box(defaultBox) {
|
||||
buttonPadding: margins(16px, 11px, 16px, 16px);
|
||||
buttonHeight: 42px;
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 42px;
|
||||
textTop: 12px;
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
shadowIgnoreTopSkip: true;
|
||||
}
|
||||
|
|
|
@ -11,38 +11,48 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h" // History::session
|
||||
#include "history/history_item.h" // HistoryItem::originalText
|
||||
#include "history/history_item_helpers.h" // DropDisallowedCustomEmoji
|
||||
#include "base/unixtime.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/rect.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "lottie/lottie_icon.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "history/view/controls/compose_controls_common.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
|
||||
|
@ -1187,10 +1197,10 @@ base::unique_qptr<Ui::RpWidget> CreateDisabledFieldView(
|
|||
return result;
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::RpWidget> TextErrorSendRestriction(
|
||||
std::unique_ptr<Ui::RpWidget> TextErrorSendRestriction(
|
||||
QWidget *parent,
|
||||
const QString &text) {
|
||||
auto result = base::make_unique_q<Ui::RpWidget>(parent);
|
||||
auto result = std::make_unique<Ui::RpWidget>(parent);
|
||||
const auto raw = result.get();
|
||||
const auto label = CreateChild<Ui::FlatLabel>(
|
||||
result.get(),
|
||||
|
@ -1215,11 +1225,11 @@ base::unique_qptr<Ui::RpWidget> TextErrorSendRestriction(
|
|||
return result;
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::RpWidget> PremiumRequiredSendRestriction(
|
||||
std::unique_ptr<Ui::RpWidget> PremiumRequiredSendRestriction(
|
||||
QWidget *parent,
|
||||
not_null<UserData*> user,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
auto result = base::make_unique_q<Ui::RpWidget>(parent);
|
||||
auto result = std::make_unique<Ui::RpWidget>(parent);
|
||||
const auto raw = result.get();
|
||||
const auto label = CreateChild<Ui::FlatLabel>(
|
||||
result.get(),
|
||||
|
@ -1254,6 +1264,196 @@ base::unique_qptr<Ui::RpWidget> PremiumRequiredSendRestriction(
|
|||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Ui::AbstractButton> BoostsToLiftWriteRestriction(
|
||||
not_null<QWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
int boosts) {
|
||||
auto result = std::make_unique<Ui::FlatButton>(
|
||||
parent,
|
||||
tr::lng_restricted_boost_group(tr::now),
|
||||
st::historyComposeButton);
|
||||
result->setClickedCallback([=] {
|
||||
const auto window = show->resolveWindow();
|
||||
window->resolveBoostState(peer->asChannel(), boosts);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Ui::AbstractButton> FrozenWriteRestriction(
|
||||
not_null<QWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
FrozenWriteRestrictionType type,
|
||||
FreezeInfoStyleOverride st) {
|
||||
using namespace Ui;
|
||||
|
||||
auto result = std::make_unique<FlatButton>(
|
||||
parent,
|
||||
QString(),
|
||||
st::historyComposeButton);
|
||||
const auto raw = result.get();
|
||||
|
||||
const auto bar = (type == FrozenWriteRestrictionType::DialogsList);
|
||||
const auto title = CreateChild<FlatLabel>(
|
||||
raw,
|
||||
(bar ? tr::lng_frozen_bar_title : tr::lng_frozen_restrict_title)(
|
||||
tr::now),
|
||||
bar ? st::frozenBarTitle : st::frozenRestrictionTitle);
|
||||
title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
title->show();
|
||||
const auto subtitle = CreateChild<FlatLabel>(
|
||||
raw,
|
||||
(bar
|
||||
? tr::lng_frozen_bar_text(
|
||||
lt_arrow,
|
||||
rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_frozen_restrict_text(Ui::Text::WithEntities)),
|
||||
bar ? st::frozenBarSubtitle : st::frozenRestrictionSubtitle);
|
||||
subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
subtitle->show();
|
||||
|
||||
const auto shadow = bar ? CreateChild<PlainShadow>(raw) : nullptr;
|
||||
const auto icon = bar ? CreateChild<RpWidget>(raw) : nullptr;
|
||||
if (icon) {
|
||||
icon->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(icon);
|
||||
st::menuIconDisableAttention.paintInCenter(p, icon->rect());
|
||||
}, icon->lifetime());
|
||||
icon->show();
|
||||
}
|
||||
|
||||
raw->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
if (bar) {
|
||||
const auto toggle = [&](auto &&widget, bool shown) {
|
||||
if (widget->isHidden() == shown) {
|
||||
widget->setVisible(shown);
|
||||
}
|
||||
};
|
||||
const auto small = 2 * st::defaultDialogRow.photoSize;
|
||||
const auto shown = (size.width() > small);
|
||||
toggle(icon, !shown);
|
||||
toggle(title, shown);
|
||||
toggle(subtitle, shown);
|
||||
icon->setGeometry(0, 0, size.width(), size.height());
|
||||
}
|
||||
const auto skip = bar
|
||||
? st::defaultDialogRow.padding.left()
|
||||
: 2 * st::normalFont->spacew;
|
||||
const auto available = size.width() - skip * 2;
|
||||
title->resizeToWidth(available);
|
||||
subtitle->resizeToWidth(available);
|
||||
const auto height = title->height() + subtitle->height();
|
||||
const auto top = (size.height() - height) / 2;
|
||||
title->moveToLeft(skip, top, size.width());
|
||||
subtitle->moveToLeft(skip, top + title->height(), size.width());
|
||||
|
||||
const auto line = st::lineWidth;
|
||||
if (shadow) {
|
||||
shadow->setGeometry(0, size.height() - line, size.width(), line);
|
||||
}
|
||||
}, title->lifetime());
|
||||
|
||||
const auto info = show->session().frozen();
|
||||
const auto detailsBox = [=](not_null<GenericBox*> box) {
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setStyle(st::frozenInfoBox);
|
||||
box->setNoContentMargin(true);
|
||||
box->addTopButton(st::boxTitleClose, [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
const auto content = box->verticalLayout();
|
||||
auto icon = Settings::CreateLottieIcon(
|
||||
content,
|
||||
{
|
||||
.name = u"media_forbidden"_q,
|
||||
.sizeOverride = {
|
||||
st::changePhoneIconSize,
|
||||
st::changePhoneIconSize,
|
||||
},
|
||||
},
|
||||
st::settingLocalPasscodeIconPadding);
|
||||
content->add(std::move(icon.widget));
|
||||
box->setShowFinishedCallback([animate = std::move(icon.animate)] {
|
||||
animate(anim::repeat::once);
|
||||
});
|
||||
|
||||
Ui::AddSkip(content);
|
||||
|
||||
const auto infoRow = [&](
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<TextWithEntities> text,
|
||||
not_null<const style::icon*> icon) {
|
||||
auto raw = content->add(
|
||||
object_ptr<Ui::VerticalLayout>(content));
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(title) | Ui::Text::ToBold(),
|
||||
st.infoTitle ? *st.infoTitle : st::defaultFlatLabel),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(text),
|
||||
st.infoAbout ? *st.infoAbout : st::upgradeGiftSubtext),
|
||||
st::settingsPremiumRowAboutPadding);
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
raw,
|
||||
*icon,
|
||||
st::starrefInfoIconPosition);
|
||||
};
|
||||
|
||||
content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_frozen_title(),
|
||||
st.title ? *st.title : st::uniqueGiftTitle),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
|
||||
Ui::AddSkip(content, st::defaultVerticalListSkip * 3);
|
||||
|
||||
infoRow(
|
||||
tr::lng_frozen_subtitle1(),
|
||||
tr::lng_frozen_text1(Text::WithEntities),
|
||||
st.violationIcon ? st.violationIcon : &st::menuIconBlock);
|
||||
infoRow(
|
||||
tr::lng_frozen_subtitle2(),
|
||||
tr::lng_frozen_text2(Text::WithEntities),
|
||||
st.readOnlyIcon ? st.readOnlyIcon : &st::menuIconLock);
|
||||
infoRow(
|
||||
tr::lng_frozen_subtitle3(),
|
||||
tr::lng_frozen_text3(
|
||||
lt_link,
|
||||
rpl::single(Text::Link(u"@SpamBot"_q, info.appealUrl)),
|
||||
lt_date,
|
||||
rpl::single(TextWithEntities{
|
||||
langDayOfMonthFull(
|
||||
base::unixtime::parse(info.until).date()),
|
||||
}),
|
||||
Text::WithEntities),
|
||||
st.appealIcon ? st.appealIcon : &st::menuIconHourglass);
|
||||
|
||||
const auto button = box->addButton(
|
||||
tr::lng_frozen_appeal_button(),
|
||||
[url = info.appealUrl] { UrlClickHandler::Open(url); });
|
||||
const auto buttonPadding = st::frozenInfoBox.buttonPadding;
|
||||
const auto buttonWidth = st::boxWideWidth
|
||||
- buttonPadding.left()
|
||||
- buttonPadding.right();
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
};
|
||||
raw->setClickedCallback([=] {
|
||||
show->show(Box(detailsBox));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void SelectTextInFieldWithMargins(
|
||||
not_null<Ui::InputField*> field,
|
||||
const TextSelection &selection) {
|
||||
|
|
|
@ -37,6 +37,10 @@ enum class PauseReason;
|
|||
class Show;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
struct WriteRestriction;
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
namespace Ui {
|
||||
class PopupMenu;
|
||||
class Show;
|
||||
|
@ -162,13 +166,41 @@ private:
|
|||
[[nodiscard]] base::unique_qptr<Ui::RpWidget> CreateDisabledFieldView(
|
||||
QWidget *parent,
|
||||
not_null<PeerData*> peer);
|
||||
[[nodiscard]] base::unique_qptr<Ui::RpWidget> TextErrorSendRestriction(
|
||||
[[nodiscard]] std::unique_ptr<Ui::RpWidget> TextErrorSendRestriction(
|
||||
QWidget *parent,
|
||||
const QString &text);
|
||||
[[nodiscard]] base::unique_qptr<Ui::RpWidget> PremiumRequiredSendRestriction(
|
||||
[[nodiscard]] std::unique_ptr<Ui::RpWidget> PremiumRequiredSendRestriction(
|
||||
QWidget *parent,
|
||||
not_null<UserData*> user,
|
||||
not_null<Window::SessionController*> controller);
|
||||
[[nodiscard]] auto BoostsToLiftWriteRestriction(
|
||||
not_null<QWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
int boosts)
|
||||
-> std::unique_ptr<Ui::AbstractButton>;
|
||||
|
||||
struct FreezeInfoStyleOverride {
|
||||
const style::Box *box = nullptr;
|
||||
const style::FlatLabel *title = nullptr;
|
||||
const style::FlatLabel *subtitle = nullptr;
|
||||
const style::icon *violationIcon = nullptr;
|
||||
const style::icon *readOnlyIcon = nullptr;
|
||||
const style::icon *appealIcon = nullptr;
|
||||
const style::FlatLabel *infoTitle = nullptr;
|
||||
const style::FlatLabel *infoAbout = nullptr;
|
||||
};
|
||||
[[nodiscard]] FreezeInfoStyleOverride DarkFreezeInfoStyle();
|
||||
|
||||
enum class FrozenWriteRestrictionType {
|
||||
MessageField,
|
||||
DialogsList,
|
||||
};
|
||||
[[nodiscard]] std::unique_ptr<Ui::AbstractButton> FrozenWriteRestriction(
|
||||
not_null<QWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
FrozenWriteRestrictionType type,
|
||||
FreezeInfoStyleOverride st = {});
|
||||
|
||||
void SelectTextInFieldWithMargins(
|
||||
not_null<Ui::InputField*> field,
|
||||
|
|
|
@ -118,7 +118,10 @@ bool CanSendAnyOf(
|
|||
not_null<const PeerData*> peer,
|
||||
ChatRestrictions rights,
|
||||
bool forbidInForums) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
if (peer->session().frozen()
|
||||
&& !peer->isFreezeAppealChat()) {
|
||||
return false;
|
||||
} else if (const auto user = peer->asUser()) {
|
||||
if (user->isInaccessible()
|
||||
|| user->isRepliesChat()
|
||||
|| user->isVerifyCodes()) {
|
||||
|
@ -178,7 +181,13 @@ SendError RestrictionError(
|
|||
not_null<PeerData*> peer,
|
||||
ChatRestriction restriction) {
|
||||
using Flag = ChatRestriction;
|
||||
if (const auto restricted = peer->amRestricted(restriction)) {
|
||||
if (peer->session().frozen()
|
||||
&& !peer->isFreezeAppealChat()) {
|
||||
return SendError({
|
||||
.text = tr::lng_frozen_restrict_title(tr::now),
|
||||
.frozen = true,
|
||||
});
|
||||
} else if (const auto restricted = peer->amRestricted(restriction)) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
if (user->requiresPremiumToWrite()
|
||||
&& !user->session().premium()) {
|
||||
|
|
|
@ -191,16 +191,19 @@ struct SendError {
|
|||
QString text;
|
||||
int boostsToLift = 0;
|
||||
bool premiumToLift = false;
|
||||
bool frozen = false;
|
||||
};
|
||||
SendError(Args &&args)
|
||||
: text(std::move(args.text))
|
||||
, boostsToLift(args.boostsToLift)
|
||||
, premiumToLift(args.premiumToLift) {
|
||||
, premiumToLift(args.premiumToLift)
|
||||
, frozen(args.frozen) {
|
||||
}
|
||||
|
||||
QString text;
|
||||
int boostsToLift = 0;
|
||||
bool premiumToLift = false;
|
||||
bool frozen = false;
|
||||
|
||||
[[nodiscard]] SendError value_or(SendError other) const {
|
||||
return *this ? *this : other;
|
||||
|
|
|
@ -1349,6 +1349,10 @@ bool PeerData::isVerifyCodes() const {
|
|||
return (id == kVerifyCodesId);
|
||||
}
|
||||
|
||||
bool PeerData::isFreezeAppealChat() const {
|
||||
return username().compare(u"spambot"_q, Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
|
||||
bool PeerData::sharedMediaInfo() const {
|
||||
return isSelf() || isRepliesChat();
|
||||
}
|
||||
|
|
|
@ -235,6 +235,7 @@ public:
|
|||
[[nodiscard]] bool isGigagroup() const;
|
||||
[[nodiscard]] bool isRepliesChat() const;
|
||||
[[nodiscard]] bool isVerifyCodes() const;
|
||||
[[nodiscard]] bool isFreezeAppealChat() const;
|
||||
[[nodiscard]] bool sharedMediaInfo() const;
|
||||
[[nodiscard]] bool savedSublistsInfo() const;
|
||||
[[nodiscard]] bool hasStoriesHidden() const;
|
||||
|
|
|
@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session_settings.h"
|
||||
#include "api/api_chat_filters.h"
|
||||
#include "apiwrap.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "core/application.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "core/update_checker.h"
|
||||
|
@ -680,6 +681,8 @@ Widget::Widget(
|
|||
|| !controller->enoughSpaceForFilters())) {
|
||||
toggleFiltersMenu(true);
|
||||
}
|
||||
|
||||
setupFrozenAccountBar();
|
||||
}
|
||||
|
||||
void Widget::setupSwipeBack() {
|
||||
|
@ -990,6 +993,29 @@ void Widget::setupTouchChatPreview() {
|
|||
}, _inner->lifetime());
|
||||
}
|
||||
|
||||
void Widget::setupFrozenAccountBar() {
|
||||
session().frozenValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateFrozenAccountBar();
|
||||
updateControlsGeometry();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void Widget::updateFrozenAccountBar() {
|
||||
if (_layout == Layout::Child
|
||||
|| _openedForum
|
||||
|| _openedFolder
|
||||
|| !session().frozen()) {
|
||||
_frozenAccountBar = nullptr;
|
||||
} else if (!_frozenAccountBar) {
|
||||
_frozenAccountBar = FrozenWriteRestriction(
|
||||
this,
|
||||
controller()->uiShow(),
|
||||
FrozenWriteRestrictionType::DialogsList);
|
||||
_frozenAccountBar->show();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setupMoreChatsBar() {
|
||||
if (_layout == Layout::Child) {
|
||||
return;
|
||||
|
@ -1418,6 +1444,9 @@ void Widget::updateControlsVisibility(bool fast) {
|
|||
if (_moreChatsBar) {
|
||||
_moreChatsBar->show();
|
||||
}
|
||||
if (_frozenAccountBar) {
|
||||
_frozenAccountBar->show();
|
||||
}
|
||||
if (_chatFilters) {
|
||||
_chatFilters->show();
|
||||
}
|
||||
|
@ -1736,6 +1765,7 @@ void Widget::changeOpenedFolder(Data::Folder *folder, anim::type animated) {
|
|||
if (_stories) {
|
||||
storiesExplicitCollapse();
|
||||
}
|
||||
updateFrozenAccountBar();
|
||||
}, (folder != nullptr), animated);
|
||||
}
|
||||
|
||||
|
@ -1792,6 +1822,7 @@ void Widget::changeOpenedForum(Data::Forum *forum, anim::type animated) {
|
|||
_api.request(base::take(_topicSearchRequest)).cancel();
|
||||
_inner->changeOpenedForum(forum);
|
||||
storiesToggleExplicitExpand(false);
|
||||
updateFrozenAccountBar();
|
||||
updateStoriesVisibility();
|
||||
}, (forum != nullptr), animated);
|
||||
}
|
||||
|
@ -2123,6 +2154,9 @@ void Widget::startWidthAnimation() {
|
|||
}
|
||||
_widthAnimationCache = grabNonNarrowScrollFrame();
|
||||
_scroll->hide();
|
||||
if (_frozenAccountBar) {
|
||||
_frozenAccountBar->hide();
|
||||
}
|
||||
if (_chatFilters) {
|
||||
_chatFilters->hide();
|
||||
}
|
||||
|
@ -2133,6 +2167,9 @@ void Widget::stopWidthAnimation() {
|
|||
_widthAnimationCache = QPixmap();
|
||||
if (!_showAnimation) {
|
||||
_scroll->setVisible(!_suggestions);
|
||||
if (_frozenAccountBar) {
|
||||
_frozenAccountBar->setVisible(!_suggestions);
|
||||
}
|
||||
if (_chatFilters) {
|
||||
_chatFilters->setVisible(!_suggestions);
|
||||
}
|
||||
|
@ -2230,6 +2267,9 @@ void Widget::startSlideAnimation(
|
|||
if (_moreChatsBar) {
|
||||
_moreChatsBar->hide();
|
||||
}
|
||||
if (_frozenAccountBar) {
|
||||
_frozenAccountBar->hide();
|
||||
}
|
||||
if (_chatFilters) {
|
||||
_chatFilters->hide();
|
||||
}
|
||||
|
@ -3813,9 +3853,17 @@ void Widget::updateControlsGeometry() {
|
|||
if (_chatFilters) {
|
||||
_chatFilters->resizeToWidth(barw);
|
||||
}
|
||||
if (_frozenAccountBar) {
|
||||
_frozenAccountBar->resize(barw, _frozenAccountBar->height());
|
||||
}
|
||||
_updateScrollGeometryCached = [=] {
|
||||
const auto moreChatsBarTop = expandedStoriesTop
|
||||
const auto frozenBarTop = expandedStoriesTop
|
||||
+ ((!_stories || _stories->isHidden()) ? 0 : _aboveScrollAdded);
|
||||
if (_frozenAccountBar) {
|
||||
_frozenAccountBar->move(0, frozenBarTop);
|
||||
}
|
||||
const auto moreChatsBarTop = frozenBarTop
|
||||
+ (_frozenAccountBar ? _frozenAccountBar->height() : 0);
|
||||
if (_moreChatsBar) {
|
||||
_moreChatsBar->move(0, moreChatsBarTop);
|
||||
}
|
||||
|
|
|
@ -201,6 +201,7 @@ private:
|
|||
|
||||
void setupSupportMode();
|
||||
void setupTouchChatPreview();
|
||||
void setupFrozenAccountBar();
|
||||
void setupConnectingWidget();
|
||||
void setupMainMenuToggle();
|
||||
void setupMoreChatsBar();
|
||||
|
@ -223,6 +224,7 @@ private:
|
|||
void showMainMenu();
|
||||
void clearSearchCache(bool clearPosts);
|
||||
void setSearchQuery(const QString &query, int cursorPosition = -1);
|
||||
void updateFrozenAccountBar();
|
||||
void updateControlsVisibility(bool fast = false);
|
||||
void updateLockUnlockVisibility(
|
||||
anim::type animated = anim::type::instant);
|
||||
|
@ -300,6 +302,9 @@ private:
|
|||
|
||||
const Layout _layout = Layout::Main;
|
||||
int _narrowWidth = 0;
|
||||
|
||||
std::unique_ptr<Ui::AbstractButton> _frozenAccountBar;
|
||||
|
||||
object_ptr<Ui::RpWidget> _searchControls;
|
||||
object_ptr<HistoryView::TopBarWidget> _subsectionTopBar = { nullptr };
|
||||
struct {
|
||||
|
|
|
@ -6514,21 +6514,24 @@ void HistoryWidget::updateSendRestriction() {
|
|||
_sendRestrictionKey = restriction.text;
|
||||
if (!restriction) {
|
||||
_sendRestriction = nullptr;
|
||||
} else if (restriction.frozen) {
|
||||
const auto show = controller()->uiShow();
|
||||
_sendRestriction = FrozenWriteRestriction(
|
||||
this,
|
||||
show,
|
||||
FrozenWriteRestrictionType::MessageField);
|
||||
} else if (restriction.premiumToLift) {
|
||||
_sendRestriction = PremiumRequiredSendRestriction(
|
||||
this,
|
||||
_peer->asUser(),
|
||||
controller());
|
||||
} else if (const auto lifting = restriction.boostsToLift) {
|
||||
auto button = base::make_unique_q<Ui::FlatButton>(
|
||||
const auto show = controller()->uiShow();
|
||||
_sendRestriction = BoostsToLiftWriteRestriction(
|
||||
this,
|
||||
restriction.text,
|
||||
st::historyComposeButton);
|
||||
const auto channel = _peer->asChannel();
|
||||
button->setClickedCallback([=] {
|
||||
controller()->resolveBoostState(channel, lifting);
|
||||
});
|
||||
_sendRestriction = std::move(button);
|
||||
show,
|
||||
_peer,
|
||||
lifting);
|
||||
} else {
|
||||
_sendRestriction = TextErrorSendRestriction(this, restriction.text);
|
||||
}
|
||||
|
|
|
@ -820,7 +820,7 @@ private:
|
|||
bool _cmdStartShown = false;
|
||||
object_ptr<Ui::InputField> _field;
|
||||
base::unique_qptr<Ui::RpWidget> _fieldDisabled;
|
||||
base::unique_qptr<Ui::RpWidget> _sendRestriction;
|
||||
std::unique_ptr<Ui::RpWidget> _sendRestriction;
|
||||
using CharactersLimitLabel = HistoryView::Controls::CharactersLimitLabel;
|
||||
base::unique_qptr<CharactersLimitLabel> _charsLimitation;
|
||||
QString _sendRestrictionKey;
|
||||
|
|
|
@ -40,6 +40,7 @@ enum class WriteRestrictionType {
|
|||
None,
|
||||
Rights,
|
||||
PremiumRequired,
|
||||
Frozen,
|
||||
};
|
||||
|
||||
struct WriteRestriction {
|
||||
|
|
|
@ -2324,15 +2324,17 @@ void SetupRestrictionView(
|
|||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](Controls::WriteRestriction value) {
|
||||
using Type = Controls::WriteRestriction::Type;
|
||||
if (const auto lifting = value.boostsToLift) {
|
||||
state->button = std::make_unique<Ui::FlatButton>(
|
||||
if (value.type == Type::Frozen) {
|
||||
state->button = FrozenWriteRestriction(
|
||||
widget,
|
||||
tr::lng_restricted_boost_group(tr::now),
|
||||
st::historyComposeButton);
|
||||
state->button->setClickedCallback([=] {
|
||||
const auto window = show->resolveWindow();
|
||||
window->resolveBoostState(peer->asChannel(), lifting);
|
||||
});
|
||||
show,
|
||||
FrozenWriteRestrictionType::MessageField);
|
||||
} else if (const auto lifting = value.boostsToLift) {
|
||||
state->button = BoostsToLiftWriteRestriction(
|
||||
widget,
|
||||
show,
|
||||
peer,
|
||||
lifting);
|
||||
} else if (value.type == Type::Rights) {
|
||||
state->icon = nullptr;
|
||||
state->unlock = nullptr;
|
||||
|
|
|
@ -671,12 +671,22 @@ void RepliesWidget::setupComposeControls() {
|
|||
: tr::lng_forum_topic_closed(tr::now);
|
||||
});
|
||||
auto writeRestriction = rpl::combine(
|
||||
session().frozenValue(),
|
||||
session().changes().peerFlagsValue(
|
||||
_history->peer,
|
||||
Data::PeerUpdate::Flag::Rights),
|
||||
Data::CanSendAnythingValue(_history->peer),
|
||||
std::move(topicWriteRestrictions)
|
||||
) | rpl::map([=](auto, auto, Data::SendError topicRestriction) {
|
||||
) | rpl::map([=](
|
||||
const Main::FreezeInfo &info,
|
||||
auto,
|
||||
auto,
|
||||
Data::SendError topicRestriction) {
|
||||
if (info) {
|
||||
return Controls::WriteRestriction{
|
||||
.type = Controls::WriteRestrictionType::Frozen,
|
||||
};
|
||||
}
|
||||
const auto allWithoutPolls = Data::AllSendRestrictions()
|
||||
& ~ChatRestriction::SendPolls;
|
||||
const auto canSendAnything = _topic
|
||||
|
|
|
@ -268,15 +268,22 @@ void ScheduledWidget::setupComposeControls() {
|
|||
: tr::lng_forum_topic_closed(tr::now);
|
||||
});
|
||||
return rpl::combine(
|
||||
session().frozenValue(),
|
||||
session().changes().peerFlagsValue(
|
||||
_history->peer,
|
||||
Data::PeerUpdate::Flag::Rights),
|
||||
Data::CanSendAnythingValue(_history->peer),
|
||||
std::move(topicWriteRestrictions)
|
||||
) | rpl::map([=](
|
||||
const Main::FreezeInfo &info,
|
||||
auto,
|
||||
auto,
|
||||
Data::SendError topicRestriction) {
|
||||
if (info) {
|
||||
return Controls::WriteRestriction{
|
||||
.type = Controls::WriteRestrictionType::Frozen,
|
||||
};
|
||||
}
|
||||
const auto allWithoutPolls = Data::AllSendRestrictions()
|
||||
& ~ChatRestriction::SendPolls;
|
||||
const auto canSendAnything = Data::CanSendAnyOf(
|
||||
|
@ -303,11 +310,17 @@ void ScheduledWidget::setupComposeControls() {
|
|||
}()
|
||||
: [&] {
|
||||
return rpl::combine(
|
||||
session().frozenValue(),
|
||||
session().changes().peerFlagsValue(
|
||||
_history->peer,
|
||||
Data::PeerUpdate::Flag::Rights),
|
||||
Data::CanSendAnythingValue(_history->peer)
|
||||
) | rpl::map([=] {
|
||||
) | rpl::map([=](const Main::FreezeInfo &info, auto, auto) {
|
||||
if (info) {
|
||||
return Controls::WriteRestriction{
|
||||
.type = Controls::WriteRestrictionType::Frozen,
|
||||
};
|
||||
}
|
||||
const auto allWithoutPolls = Data::AllSendRestrictions()
|
||||
& ~ChatRestriction::SendPolls;
|
||||
const auto canSendAnything = Data::CanSendAnyOf(
|
||||
|
|
|
@ -24,6 +24,7 @@ AppConfig::AppConfig(not_null<Account*> account) : _account(account) {
|
|||
) | rpl::filter([=](Session *session) {
|
||||
return (session != nullptr);
|
||||
}) | rpl::start_with_next([=] {
|
||||
_lastFrozenRefresh = 0;
|
||||
refresh();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
@ -35,6 +36,18 @@ void AppConfig::start() {
|
|||
) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
|
||||
_api.emplace(instance);
|
||||
refresh();
|
||||
|
||||
_frozenTrackLifetime = instance->frozenErrorReceived(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (!get<int>(u"freeze_since_date"_q, 0)) {
|
||||
const auto now = crl::now();
|
||||
if (!_lastFrozenRefresh
|
||||
|| now > _lastFrozenRefresh + kRefreshTimeout) {
|
||||
_lastFrozenRefresh = now;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,9 @@ private:
|
|||
|
||||
std::vector<QString> _startRefPrefixes;
|
||||
|
||||
crl::time _lastFrozenRefresh = 0;
|
||||
rpl::lifetime _frozenTrackLifetime;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -160,15 +160,6 @@ Session::Session(
|
|||
}
|
||||
}, _lifetime);
|
||||
|
||||
#ifndef OS_MAC_STORE
|
||||
appConfig().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
_premiumPossible = !appConfig().get<bool>(
|
||||
u"premium_purchase_blocked"_q,
|
||||
true);
|
||||
}, _lifetime);
|
||||
#endif // OS_MAC_STORE
|
||||
|
||||
if (_settings->hadLegacyCallsPeerToPeerNobody()) {
|
||||
api().userPrivacy().save(
|
||||
Api::UserPrivacy::Key::CallsPeer2Peer,
|
||||
|
@ -204,6 +195,27 @@ Session::Session(
|
|||
_api->requestNotifySettings(MTP_inputNotifyBroadcasts());
|
||||
|
||||
Core::App().downloadManager().trackSession(this);
|
||||
|
||||
appConfig().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
appConfigRefreshed();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Session::appConfigRefreshed() {
|
||||
const auto &config = appConfig();
|
||||
|
||||
_frozen = FreezeInfo{
|
||||
.since = config.get<int>(u"freeze_since_date"_q, 0),
|
||||
.until = config.get<int>(u"freeze_until_date"_q, 0),
|
||||
.appealUrl = config.get<QString>(u"freeze_appeal_url"_q, QString()),
|
||||
};
|
||||
|
||||
#ifndef OS_MAC_STORE
|
||||
_premiumPossible = !config.get<bool>(
|
||||
u"premium_purchase_blocked"_q,
|
||||
true);
|
||||
#endif // OS_MAC_STORE
|
||||
}
|
||||
|
||||
void Session::setTmpPassword(const QByteArray &password, TimeId validUntil) {
|
||||
|
@ -431,6 +443,14 @@ Support::FastButtonsBots &Session::fastButtonsBots() const {
|
|||
return *_fastButtonsBots;
|
||||
}
|
||||
|
||||
FreezeInfo Session::frozen() const {
|
||||
return _frozen.current();
|
||||
}
|
||||
|
||||
rpl::producer<FreezeInfo> Session::frozenValue() const {
|
||||
return _frozen.value();
|
||||
}
|
||||
|
||||
void Session::addWindow(not_null<Window::SessionController*> controller) {
|
||||
_windows.emplace(controller);
|
||||
controller->lifetime().add([=] {
|
||||
|
|
|
@ -80,6 +80,19 @@ class Domain;
|
|||
class SessionSettings;
|
||||
class SendAsPeers;
|
||||
|
||||
struct FreezeInfo {
|
||||
TimeId since = 0;
|
||||
TimeId until = 0;
|
||||
QString appealUrl;
|
||||
|
||||
explicit operator bool() const {
|
||||
return since != 0;
|
||||
}
|
||||
friend inline bool operator==(
|
||||
const FreezeInfo &,
|
||||
const FreezeInfo &) = default;
|
||||
};
|
||||
|
||||
class Session final : public base::has_weak_ptr {
|
||||
public:
|
||||
Session(
|
||||
|
@ -236,12 +249,17 @@ public:
|
|||
[[nodiscard]] Support::Templates &supportTemplates() const;
|
||||
[[nodiscard]] Support::FastButtonsBots &fastButtonsBots() const;
|
||||
|
||||
[[nodiscard]] FreezeInfo frozen() const;
|
||||
[[nodiscard]] rpl::producer<FreezeInfo> frozenValue() const;
|
||||
|
||||
[[nodiscard]] auto colorIndicesValue()
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed>;
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultSaveDelay = crl::time(1000);
|
||||
|
||||
void appConfigRefreshed();
|
||||
|
||||
const UserId _userId;
|
||||
const not_null<Account*> _account;
|
||||
|
||||
|
@ -288,6 +306,8 @@ private:
|
|||
base::flat_set<not_null<Window::SessionController*>> _windows;
|
||||
base::Timer _saveSettingsTimer;
|
||||
|
||||
rpl::variable<FreezeInfo> _frozen;
|
||||
|
||||
QByteArray _tmpPassword;
|
||||
TimeId _tmpPasswordValidUntil = 0;
|
||||
|
||||
|
|
|
@ -841,7 +841,9 @@ void ReplyArea::show(
|
|||
peer
|
||||
) | rpl::map([=](bool can) {
|
||||
using namespace HistoryView::Controls;
|
||||
return (can
|
||||
return user->session().frozen()
|
||||
? WriteRestriction{ .type = WriteRestrictionType::Frozen }
|
||||
: (can
|
||||
|| !user
|
||||
|| !user->requiresPremiumToWrite()
|
||||
|| user->session().premium())
|
||||
|
|
|
@ -100,6 +100,7 @@ public:
|
|||
|
||||
[[nodiscard]] auto nonPremiumDelayedRequests() const
|
||||
-> rpl::producer<mtpRequestId>;
|
||||
[[nodiscard]] rpl::producer<> frozenErrorReceived() const;
|
||||
|
||||
void restart();
|
||||
void restart(ShiftedDcId shiftedDcId);
|
||||
|
@ -286,6 +287,7 @@ private:
|
|||
Fn<void(ShiftedDcId shiftedDcId)> _sessionResetHandler;
|
||||
|
||||
rpl::event_stream<mtpRequestId> _nonPremiumDelayedRequests;
|
||||
rpl::event_stream<> _frozenErrorReceived;
|
||||
|
||||
base::Timer _checkDelayedTimer;
|
||||
|
||||
|
@ -562,6 +564,10 @@ auto Instance::Private::nonPremiumDelayedRequests() const
|
|||
return _nonPremiumDelayedRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> Instance::Private::frozenErrorReceived() const {
|
||||
return _frozenErrorReceived.events();
|
||||
}
|
||||
|
||||
void Instance::Private::requestConfigIfOld() {
|
||||
const auto timeout = _config->values().blockedMode
|
||||
? kConfigBecomesOldForBlockedIn
|
||||
|
@ -1593,6 +1599,8 @@ bool Instance::Private::onErrorDefault(
|
|||
return true;
|
||||
} else if (type == u"CONNECTION_LANG_CODE_INVALID"_q) {
|
||||
Lang::CurrentCloudManager().resetToDefault();
|
||||
} else if (type == u"FROZEN_METHOD_INVALID"_q) {
|
||||
_frozenErrorReceived.fire({});
|
||||
}
|
||||
if (badGuestDc) _badGuestDcRequests.erase(requestId);
|
||||
return false;
|
||||
|
@ -1920,6 +1928,10 @@ rpl::producer<mtpRequestId> Instance::nonPremiumDelayedRequests() const {
|
|||
return _private->nonPremiumDelayedRequests();
|
||||
}
|
||||
|
||||
rpl::producer<> Instance::frozenErrorReceived() const {
|
||||
return _private->frozenErrorReceived();
|
||||
}
|
||||
|
||||
void Instance::requestConfigIfOld() {
|
||||
_private->requestConfigIfOld();
|
||||
}
|
||||
|
|
|
@ -142,6 +142,7 @@ public:
|
|||
|
||||
[[nodiscard]] auto nonPremiumDelayedRequests() const
|
||||
-> rpl::producer<mtpRequestId>;
|
||||
[[nodiscard]] rpl::producer<> frozenErrorReceived() const;
|
||||
|
||||
void syncHttpUnixtime();
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ menuIconUnique: icon {{ "menu/unique", menuIconColor }};
|
|||
menuIconNftWear: icon {{ "menu/nft_wear", menuIconColor }};
|
||||
menuIconNftTakeOff: icon {{ "menu/nft_takeoff", menuIconColor }};
|
||||
menuIconShortcut: icon {{ "menu/shortcut", menuIconColor }};
|
||||
menuIconHourglass: icon {{ "menu/hourglass", menuIconColor }};
|
||||
|
||||
menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
|
||||
menuIconTTLAnyTextPosition: point(11px, 22px);
|
||||
|
|
Loading…
Add table
Reference in a new issue