Show explaining preview of forwards privacy.

This commit is contained in:
John Preston 2019-03-19 17:50:36 +04:00
parent 81862215b4
commit b972da059a
14 changed files with 327 additions and 33 deletions

View File

@ -650,6 +650,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_forwards_exceptions" = "These settings will override the values above.";
"lng_edit_privacy_forwards_always_title" = "Always allow";
"lng_edit_privacy_forwards_never_title" = "Never allow";
"lng_edit_privacy_forwards_sample_message" = "Reinhardt, we need to find you some new tunes 🎶";
"lng_edit_privacy_forwards_sample_everyone" = "Link to your account.";
"lng_edit_privacy_forwards_sample_contacts" = "Link if allowed by settings below.";
"lng_edit_privacy_forwards_sample_nobody" = "Not a link to your account.";
"lng_edit_privacy_profile_photo_title" = "Profile photo privacy";
"lng_edit_privacy_profile_photo_header" = "Who can see my profile photo";

View File

@ -390,12 +390,12 @@ BackgroundPreviewBox::BackgroundPreviewBox(
QWidget*,
const Data::WallPaper &paper)
: _text1(GenerateTextItem(
this,
delegate(),
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
lang(lng_background_text1),
false))
, _text2(GenerateTextItem(
this,
delegate(),
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
lang(lng_background_text2),
true))
@ -404,6 +404,10 @@ BackgroundPreviewBox::BackgroundPreviewBox(
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
}
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
return static_cast<HistoryView::ElementDelegate*>(this);
}
void BackgroundPreviewBox::prepare() {
setTitle(langFactory(lng_background_header));
@ -765,7 +769,7 @@ HistoryView::Context BackgroundPreviewBox::elementContext() {
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
not_null<HistoryMessage*> message) {
return std::make_unique<HistoryView::Message>(this, message);
return std::make_unique<HistoryView::Message>(delegate(), message);
}
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(

View File

@ -20,7 +20,7 @@ class Checkbox;
class BackgroundPreviewBox
: public BoxContent
, public HistoryView::ElementDelegate {
, private HistoryView::ElementDelegate {
public:
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
@ -28,7 +28,14 @@ public:
const QString &slug,
const QMap<QString, QString> &params);
protected:
void prepare() override;
void paintEvent(QPaintEvent *e) override;
private:
using Element = HistoryView::Element;
not_null<HistoryView::ElementDelegate*> delegate();
HistoryView::Context elementContext() override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryMessage*> message) override;
@ -41,12 +48,6 @@ public:
not_null<const Element*> element) override;
bool elementInSelectionMode() override;
protected:
void prepare() override;
void paintEvent(QPaintEvent *e) override;
private:
void apply();
void share();
void step_radial(crl::time ms, bool timer);

View File

@ -239,12 +239,12 @@ void EditPrivacyBox::setupContent() {
const auto group = std::make_shared<Ui::RadioenumGroup<Option>>(
_value.option);
const auto toggle = Ui::CreateChild<rpl::event_stream<>>(content);
const auto toggle = Ui::CreateChild<rpl::event_stream<Option>>(content);
group->setChangedCallback([=](Option value) {
_value.option = value;
toggle->fire({});
toggle->fire_copy(value);
});
auto optionValue = toggle->events_starting_with_copy(_value.option);
const auto addOptionRow = [&](Option option) {
return (_controller->hasOption(option) || (_value.option == option))
@ -275,8 +275,8 @@ void EditPrivacyBox::setupContent() {
std::move(label),
st::settingsButton,
text);
button->toggleOn(toggle->events_starting_with(
rpl::empty_value()
button->toggleOn(rpl::duplicate(
optionValue
) | rpl::map([=] {
return showExceptionLink(exception);
}))->entity()->addClickHandler([=] {
@ -285,6 +285,13 @@ void EditPrivacyBox::setupContent() {
return button;
};
auto above = _controller->setupAboveWidget(
content,
std::move(optionValue));
if (above) {
content->add(std::move(above));
}
AddSubsectionTitle(content, _controller->optionsTitleKey());
addOptionRow(Option::Everyone);
addOptionRow(Option::Contacts);

View File

@ -38,21 +38,30 @@ public:
public:
using Key = ApiWrap::Privacy::Key;
virtual Key key() = 0;
virtual MTPInputPrivacyKey apiKey() = 0;
[[nodiscard]] virtual Key key() = 0;
[[nodiscard]] virtual MTPInputPrivacyKey apiKey() = 0;
virtual QString title() = 0;
virtual bool hasOption(Option option) {
[[nodiscard]] virtual QString title() = 0;
[[nodiscard]] virtual bool hasOption(Option option) {
return true;
}
virtual LangKey optionsTitleKey() = 0;
virtual LangKey optionLabelKey(Option option);
virtual rpl::producer<QString> warning() {
[[nodiscard]] virtual LangKey optionsTitleKey() = 0;
[[nodiscard]] virtual LangKey optionLabelKey(Option option);
[[nodiscard]] virtual rpl::producer<QString> warning() {
return rpl::never<QString>();
}
virtual LangKey exceptionButtonTextKey(Exception exception) = 0;
virtual QString exceptionBoxTitle(Exception exception) = 0;
virtual rpl::producer<QString> exceptionsDescription() = 0;
[[nodiscard]] virtual LangKey exceptionButtonTextKey(
Exception exception) = 0;
[[nodiscard]] virtual QString exceptionBoxTitle(
Exception exception) = 0;
[[nodiscard]] virtual auto exceptionsDescription()
-> rpl::producer<QString> = 0;
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupAboveWidget(
not_null<QWidget*> parent,
rpl::producer<Option> option) {
return { nullptr };
}
virtual void confirmSave(
bool someAreDisallowed,

View File

@ -399,7 +399,7 @@ void Widget::paintEvent(QPaintEvent *e) {
//auto ms = crl::now();
//_historyDownShown.step(ms);
SectionWidget::PaintBackground(this, e);
SectionWidget::PaintBackground(this, e->rect());
}
void Widget::onScroll() {

View File

@ -539,7 +539,7 @@ void Widget::paintEvent(QPaintEvent *e) {
const auto ms = crl::now();
_scrollDownShown.step(ms);
SectionWidget::PaintBackground(this, e);
SectionWidget::PaintBackground(this, e->rect());
if (_emptyTextView) {
Painter p(this);

View File

@ -6563,7 +6563,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
updateListSize();
}
Window::SectionWidget::PaintBackground(this, e);
Window::SectionWidget::PaintBackground(this, e->rect());
Painter p(this);
const auto clip = e->rect();

View File

@ -583,7 +583,8 @@ void Message::paintFromName(
void Message::paintForwardedInfo(Painter &p, QRect &trect, bool selected) const {
if (displayForwardedFrom()) {
style::font serviceFont(st::msgServiceFont), serviceName(st::msgServiceNameFont);
const auto &serviceFont = st::msgServiceFont;
const auto &serviceName = st::msgServiceNameFont;
const auto item = message();
const auto outbg = hasOutLayout();

View File

@ -203,3 +203,8 @@ settingsAudioVolumeLabel: LabelSimple(defaultLabelSimple) {
}
settingsAudioVolumeLabelPadding: margins(22px, 11px, 22px, 11px);
settingsLevelMeterPadding: margins(23px, 10px, 20px, 10px);
settingsForwardPrivacyPadding: 8px;
settingsForwardPrivacyArrowSkip: 32px;
settingsForwardPrivacyArrowSize: 6px;
settingsForwardPrivacyTooltipPadding: margins(8px, 6px, 8px, 6px);

View File

@ -16,12 +16,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_message.h"
#include "history/history_item_components.h"
#include "history/history_message.h"
#include "history/history.h"
#include "calls/calls_instance.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/image/image_prepare.h"
#include "window/section_widget.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
@ -91,6 +100,54 @@ std::unique_ptr<BlockUserBoxController::Row> BlockUserBoxController::createRow(n
return nullptr;
}
AdminLog::OwnedItem GenerateForwardedItem(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
const QString &text) {
Expects(history->peer->isUser());
using Flag = MTPDmessage::Flag;
using FwdFlag = MTPDmessageFwdHeader::Flag;
// #TODO common global incrementable id for fake items, like clientMsgId.
static auto id = ServerMaxMsgId + (ServerMaxMsgId / 6);
const auto flags = Flag::f_from_id | Flag::f_fwd_from;
const auto replyTo = 0;
const auto viaBotId = 0;
const auto item = MTP_message(
MTP_flags(flags),
MTP_int(++id),
MTP_int(peerToUser(history->peer->id)),
peerToMTP(history->session().userPeerId()),
MTP_messageFwdHeader(
MTP_flags(FwdFlag::f_from_id),
MTP_int(history->session().userId()),
MTPstring(), // from_name
MTP_int(unixtime()),
MTPint(), // channel_id
MTPint(), // channel_post
MTPstring(), // post_author
MTPPeer(), // saved_from_peer
MTPint()), // saved_from_msg_id
MTPint(), // via_bot_id
MTPint(), // reply_to_msg_id,
MTP_int(unixtime()), // date
MTP_string(text),
MTPMessageMedia(),
MTPReplyMarkup(),
MTPnullEntities,
MTPint(), // views
MTPint(), // edit_date
MTPstring(), // post_author
MTPlong() // grouped_id
).match([&](const MTPDmessage & data) {
return new HistoryMessage(history, data);
}, [](auto &&) -> HistoryMessage* {
Unexpected("Type in GenerateForwardedItem.");
});
return AdminLog::OwnedItem(delegate, item);
}
} // namespace
void BlockedBoxController::prepare() {
@ -473,6 +530,186 @@ auto ForwardsPrivacyController::exceptionsDescription()
return Lang::Viewer(lng_edit_privacy_forwards_exceptions);
}
object_ptr<Ui::RpWidget> ForwardsPrivacyController::setupAboveWidget(
not_null<QWidget*> parent,
rpl::producer<Option> optionValue) {
using namespace rpl::mappers;
auto message = GenerateForwardedItem(
delegate(),
Auth().data().history(
peerFromUser(PeerData::kServiceNotificationsId)),
lang(lng_edit_privacy_forwards_sample_message));
const auto view = message.get();
auto result = object_ptr<Ui::RpWidget>(parent);
const auto widget = result.data();
Ui::AttachAsChild(widget, std::move(message));
const auto option = widget->lifetime().make_state<Option>();
const auto padding = st::settingsForwardPrivacyPadding;
widget->widthValue(
) | rpl::filter(
_1 >= (st::historyMinimalWidth / 2)
) | rpl::start_with_next([=](int width) {
const auto height = view->resizeGetHeight(width);
const auto top = view->marginTop();
const auto bottom = view->marginBottom();
const auto full = padding + bottom + height + top + padding;
widget->resize(width, full);
}, widget->lifetime());
widget->paintRequest(
) | rpl::start_with_next([=](QRect rect) {
Window::SectionWidget::PaintBackground(widget, rect);
Painter p(widget);
p.translate(0, padding + view->marginBottom());
view->draw(p, widget->rect(), TextSelection(), crl::now());
PaintForwardedTooltip(p, view, *option);
}, widget->lifetime());
std::move(
optionValue
) | rpl::start_with_next([=](Option value) {
*option = value;
widget->update();
}, widget->lifetime());
return result;
}
void ForwardsPrivacyController::PaintForwardedTooltip(
Painter &p,
not_null<HistoryView::Element*> view,
Option value) {
// This breaks HistoryView::Element encapsulation :(
const auto forwarded = view->data()->Get<HistoryMessageForwarded>();
const auto availableWidth = view->width()
- st::msgMargin.left()
- st::msgMargin.right();
const auto bubbleWidth = ranges::min({
availableWidth,
view->maxWidth(),
st::msgMaxWidth
});
const auto innerWidth = bubbleWidth
- st::msgPadding.left()
- st::msgPadding.right();
const auto phrase = lng_forwarded(
lt_user,
App::peerName(view->data()->history()->session().user()));
const auto possiblePosition = Lang::FindTagReplacementPosition(
lang(lng_forwarded__tagged),
lt_user);
const auto position = (possiblePosition >= 0
&& possiblePosition < phrase.size())
? possiblePosition
: 0;
const auto before = phrase.mid(0, position);
const auto skip = st::msgMargin.left() + st::msgPadding.left();
const auto small = forwarded->text.countHeight(innerWidth)
< 2 * st::msgServiceFont->height;
const auto nameLeft = skip + (small ? st::msgServiceFont->width(before) : 0);
const auto right = skip + innerWidth;
const auto key = [&] {
switch (value) {
case Option::Everyone:
return lng_edit_privacy_forwards_sample_everyone;
case Option::Contacts:
return lng_edit_privacy_forwards_sample_contacts;
case Option::Nobody:
return lng_edit_privacy_forwards_sample_nobody;
}
Unexpected("Option value in ForwardsPrivacyController.");
}();
const auto text = lang(key);
const auto &font = st::toastTextStyle.font;
const auto textWidth = font->width(text);
const auto arrowSkip = st::settingsForwardPrivacyArrowSkip;
const auto arrowSize = st::settingsForwardPrivacyArrowSize;
const auto fullWidth = std::max(textWidth, 2 * arrowSkip);
const auto padding = st::settingsForwardPrivacyTooltipPadding;
const auto rect = QRect(0, 0, textWidth, font->height).marginsAdded(
padding
).translated(padding.left(), padding.top());
const auto top = view->marginTop()
+ st::msgPadding.top()
+ (small ? 1 : 2) * st::msgServiceFont->height
+ arrowSize;
const auto left1 = std::min(nameLeft, right - rect.width());
const auto left2 = std::max(left1, skip);
const auto left = left2;
const auto arrowLeft1 = nameLeft + arrowSkip;
const auto arrowLeft2 = std::min(
arrowLeft1,
std::max((left + right) / 2, right - arrowSkip));
const auto arrowLeft = arrowLeft2;
const auto geometry = rect.translated(left, top);
App::roundRect(p, geometry, st::toastBg, ImageRoundRadius::Small);
p.setFont(font);
p.setPen(st::toastFg);
p.drawText(
geometry.x() + padding.left(),
geometry.y() + padding.top() + font->ascent,
text);
QPainterPath path;
path.moveTo(arrowLeft - arrowSize, top);
path.lineTo(arrowLeft, top - arrowSize);
path.lineTo(arrowLeft + arrowSize, top);
path.lineTo(arrowLeft - arrowSize, top);
{
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.fillPath(path, st::toastBg);
}
}
auto ForwardsPrivacyController::delegate()
-> not_null<HistoryView::ElementDelegate*> {
return static_cast<HistoryView::ElementDelegate*>(this);
}
HistoryView::Context ForwardsPrivacyController::elementContext() {
return HistoryView::Context::ContactPreview;
}
auto ForwardsPrivacyController::elementCreate(
not_null<HistoryMessage*> message)
-> std::unique_ptr<HistoryView::Element> {
return std::make_unique<HistoryView::Message>(delegate(), message);
}
auto ForwardsPrivacyController::elementCreate(
not_null<HistoryService*> message)
-> std::unique_ptr<HistoryView::Element> {
Unexpected("Service message in ForwardsPrivacyController.");
}
bool ForwardsPrivacyController::elementUnderCursor(
not_null<const Element*> view) {
return false;
}
void ForwardsPrivacyController::elementAnimationAutoplayAsync(
not_null<const Element*> element) {
}
crl::time ForwardsPrivacyController::elementHighlightTime(
not_null<const Element*> element) {
return crl::time(0);
}
bool ForwardsPrivacyController::elementInSelectionMode() {
return false;
}
ApiWrap::Privacy::Key ProfilePhotoPrivacyController::key() {
return Key::ProfilePhoto;
}

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_box.h"
#include "boxes/edit_privacy_box.h"
#include "history/view/history_view_element.h"
#include "mtproto/sender.h"
namespace Settings {
@ -106,7 +107,9 @@ public:
};
class ForwardsPrivacyController : public EditPrivacyBox::Controller {
class ForwardsPrivacyController
: public EditPrivacyBox::Controller
, private HistoryView::ElementDelegate {
public:
using Option = EditPrivacyBox::Option;
using Exception = EditPrivacyBox::Exception;
@ -121,6 +124,30 @@ public:
QString exceptionBoxTitle(Exception exception) override;
rpl::producer<QString> exceptionsDescription() override;
object_ptr<Ui::RpWidget> setupAboveWidget(
not_null<QWidget*> parent,
rpl::producer<Option> optionValue) override;
private:
using Element = HistoryView::Element;
not_null<HistoryView::ElementDelegate*> delegate();
HistoryView::Context elementContext() override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryMessage*> message) override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryService*> message) override;
bool elementUnderCursor(not_null<const Element*> view) override;
void elementAnimationAutoplayAsync(
not_null<const Element*> element) override;
crl::time elementHighlightTime(
not_null<const Element*> element) override;
bool elementInSelectionMode() override;
static void PaintForwardedTooltip(
Painter &p,
not_null<HistoryView::Element*> view,
Option value);
};
class ProfilePhotoPrivacyController : public EditPrivacyBox::Controller {

View File

@ -72,10 +72,9 @@ void SectionWidget::showFast() {
showFinished();
}
void SectionWidget::PaintBackground(QWidget *widget, QPaintEvent *event) {
void SectionWidget::PaintBackground(not_null<QWidget*> widget, QRect clip) {
Painter p(widget);
auto clip = event->rect();
auto fill = QRect(0, 0, widget->width(), App::main()->height());
if (const auto color = Window::Theme::Background()->colorForFill()) {
p.fillRect(fill, *color);

View File

@ -122,7 +122,7 @@ public:
return nullptr;
}
static void PaintBackground(QWidget *widget, QPaintEvent *event);
static void PaintBackground(not_null<QWidget*> widget, QRect clip);
protected:
void paintEvent(QPaintEvent *e) override;