Add terms of service to the Intro.

This commit is contained in:
John Preston 2018-05-30 18:08:12 +03:00
parent 734c410879
commit 5c5438c12e
10 changed files with 348 additions and 26 deletions

View File

@ -1493,6 +1493,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_info_feed_is_default" = "Group new channels";
"lng_info_feed_channels" = "Channels";
"lng_terms_signup" = "By signing up,\nyou agree to the {link}.";
"lng_terms_signup_link" = "Terms of Service";
"lng_terms_header" = "Terms of Service";
"lng_terms_agree" = "Agree & Continue";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@ -119,9 +119,19 @@ void ConfirmBox::prepare() {
textUpdated();
}
void ConfirmBox::setMaxLineCount(int count) {
if (_maxLineCount != count) {
_maxLineCount = count;
textUpdated();
}
}
void ConfirmBox::textUpdated() {
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right();
_textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxLabelStyle.lineHeight);
_textHeight = _text.countHeight(_textWidth);
if (_maxLineCount > 0) {
accumulate_min(_textHeight, _maxLineCount * st::boxLabelStyle.lineHeight);
}
setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
setMouseTracking(_text.hasLinks());
@ -198,7 +208,11 @@ void ConfirmBox::paintEvent(QPaintEvent *e) {
// draw box title / text
p.setPen(st::boxTextFg);
_text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), 16, style::al_left);
if (_maxLineCount > 0) {
_text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), _maxLineCount, style::al_left);
} else {
_text.drawLeft(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), style::al_left);
}
}
InformBox::InformBox(QWidget*, const QString &text, base::lambda<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, lang(lng_box_ok), std::move(closedCallback)) {

View File

@ -31,6 +31,8 @@ public:
_strictCancel = strictCancel;
}
void setMaxLineCount(int count);
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
@ -55,6 +57,7 @@ private:
void confirmed();
void init(const QString &text);
void textUpdated();
void updateHover();
QString _confirmText;
QString _cancelText;
@ -64,8 +67,7 @@ private:
Text _text;
int _textWidth = 0;
int _textHeight = 0;
void updateHover();
int _maxLineCount = 16;
QPoint _lastMousePos;

View File

@ -125,6 +125,15 @@ introResetButton: RoundButton(defaultLightButton) {
}
introResetBottom: 20px;
introTermsLabel: FlatLabel(defaultFlatLabel) {
align: align(top);
}
introTermsBottom: 20px;
introTermsContent: FlatLabel(defaultFlatLabel) {
minWidth: 285px;
}
introTermsPadding: margins(23px, 0px, 16px, 0px);
introCountryIcon: icon {{ "intro_country_dropdown", menuIconFg }};
introCountryIconPosition: point(8px, 37px);

View File

@ -48,6 +48,15 @@ Locale: ") + Platform::SystemLanguage();
UrlClickHandler::doOpen(url);
}
bool TermsAcceptRequired(const QString &countryCode) {
const auto codes = std::vector<QString>{
"AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE",
"GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT",
"RO", "SK", "SI", "ES", "SE", "GB"
};
return ranges::find(codes, countryCode.toUpper()) != end(codes);
}
} // namespace
PhoneWidget::PhoneWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
@ -64,6 +73,16 @@ PhoneWidget::PhoneWidget(QWidget *parent, Widget::Data *data) : Step(parent, dat
connect(_phone, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_code, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
connect(
_code,
&Ui::CountryCodeInput::codeChanged,
this,
&PhoneWidget::toggleTerms);
connect(
_country,
&CountryInput::codeChanged,
this,
&PhoneWidget::toggleTerms);
setTitleText(langFactory(lng_phone_title));
setDescriptionText(langFactory(lng_phone_desc));
@ -75,6 +94,10 @@ PhoneWidget::PhoneWidget(QWidget *parent, Widget::Data *data) : Step(parent, dat
}
_changed = false;
subscribe(Lang::Current().updated(), [this] {
_termsAccepted = false;
});
Messenger::Instance().destroyStaleAuthorizationKeys();
}
@ -129,6 +152,13 @@ void PhoneWidget::countryChanged() {
}
}
void PhoneWidget::toggleTerms() {
_termsAccepted = false;
InvokeQueued(this, [=] {
Step::toggleTerms(_country->iso());
});
}
void PhoneWidget::onInputChange() {
_changed = true;
hidePhoneError();
@ -143,14 +173,33 @@ void PhoneWidget::submit() {
return;
}
hidePhoneError();
const auto sendCode = [=] {
hidePhoneError();
_checkRequest->start(1000);
_checkRequest->start(1000);
_sentPhone = fullNumber();
Messenger::Instance().mtp()->setUserPhone(_sentPhone);
//_sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&PhoneWidget::phoneCheckDone), rpcFail(&PhoneWidget::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(0), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&PhoneWidget::phoneSubmitDone), rpcFail(&PhoneWidget::phoneSubmitFail));
_sentPhone = fullNumber();
Messenger::Instance().mtp()->setUserPhone(_sentPhone);
//_sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&PhoneWidget::phoneCheckDone), rpcFail(&PhoneWidget::phoneSubmitFail));
_sentRequest = MTP::send(
MTPauth_SendCode(
MTP_flags(0),
MTP_string(_sentPhone),
MTPBool(),
MTP_int(ApiId),
MTP_string(ApiHash)),
rpcDone(&PhoneWidget::phoneSubmitDone),
rpcFail(&PhoneWidget::phoneSubmitFail));
};
const auto code = _country->iso();
if (!TermsAcceptRequired(code) || _termsAccepted) {
sendCode();
} else {
acceptTerms(code, base::lambda_guarded(this, [=] {
_termsAccepted = true;
sendCode();
}));
}
}
void PhoneWidget::stopCheck() {

View File

@ -47,6 +47,7 @@ private slots:
private:
void updateSignupGeometry();
void countryChanged();
void toggleTerms();
//void phoneCheckDone(const MTPauth_CheckedPhone &result);
void phoneSubmitDone(const MTPauth_SentCode &result);
@ -66,6 +67,7 @@ private:
object_ptr<CountryInput> _country;
object_ptr<Ui::CountryCodeInput> _code;
object_ptr<Ui::PhonePartInput> _phone;
bool _termsAccepted = false;
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _signup = { nullptr };

View File

@ -41,6 +41,67 @@ namespace {
constexpr str_const kDefaultCountry = "US";
class TermsBox : public BoxContent {
public:
TermsBox(
QWidget*,
const QString &text,
base::lambda<QString()> button);
rpl::producer<> agreeClicks() const;
protected:
void prepare() override;
void keyPressEvent(QKeyEvent *e) override;
private:
QString _text;
base::lambda<QString()> _button;
rpl::event_stream<> _agreeClicks;
};
TermsBox::TermsBox(
QWidget*,
const QString &text,
base::lambda<QString()> button)
: _text(text)
, _button(button) {
}
rpl::producer<> TermsBox::agreeClicks() const {
return _agreeClicks.events();
}
void TermsBox::prepare() {
setTitle(langFactory(lng_terms_header));
addButton(_button, [=] {})->clicks(
) | rpl::start_to_stream(_agreeClicks, lifetime());
const auto content = Ui::CreateChild<Ui::PaddingWrap<Ui::FlatLabel>>(
this,
object_ptr<Ui::FlatLabel>(
this,
_text,
Ui::FlatLabel::InitType::Rich,
st::introTermsContent),
st::introTermsPadding);
content->resizeToWidth(st::boxWideWidth);
content->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWideWidth, height);
}, content->lifetime());
}
void TermsBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
_agreeClicks.fire({});
} else {
BoxContent::keyPressEvent(e);
}
}
} // namespace
Widget::Widget(QWidget *parent) : RpWidget(parent)
@ -122,7 +183,9 @@ void Widget::createLanguageLink() {
_changeLanguage->entity()->setClickedCallback([=] {
Lang::CurrentCloudManager().switchToLanguage(languageId);
});
_changeLanguage->toggle(!_resetAccount, anim::type::normal);
_changeLanguage->toggle(
!_resetAccount && !_terms,
anim::type::normal);
updateControlsGeometry();
};
@ -209,14 +272,36 @@ void Widget::historyMove(Direction direction) {
auto stepHasCover = getStep()->hasCover();
_settings->toggle(!stepHasCover, anim::type::normal);
if (_update) _update->toggle(!stepHasCover, anim::type::normal);
if (_changeLanguage) _changeLanguage->toggle(!_resetAccount, anim::type::normal);
if (_update) {
_update->toggle(!stepHasCover, anim::type::normal);
}
_next->setText([this] { return getStep()->nextButtonText(); });
if (_resetAccount) _resetAccount->hide(anim::type::normal);
if (_resetAccount) {
hideAndDestroy(std::exchange(_resetAccount, { nullptr }));
}
if (_terms) {
hideAndDestroy(std::exchange(_terms, { nullptr }));
}
if (_changeLanguage) {
_changeLanguage->toggle(
!_resetAccount && !_terms,
anim::type::normal);
}
getStep()->showAnimated(direction);
fixOrder();
}
void Widget::hideAndDestroy(object_ptr<Ui::FadeWrap<Ui::RpWidget>> widget) {
const auto weak = make_weak(widget.data());
widget->hide(anim::type::normal);
widget->shownValue(
) | rpl::start_with_next([=](bool shown) {
if (!shown && weak) {
weak->deleteLater();
}
}, widget->lifetime());
}
void Widget::fixOrder() {
_next->raise();
if (_update) _update->raise();
@ -240,30 +325,74 @@ void Widget::moveToStep(Step *step, Direction direction) {
void Widget::appendStep(Step *step) {
_stepHistory.push_back(step);
step->setGeometry(calculateStepRect());
step->setGoCallback([this](Step *step, Direction direction) {
step->setGoCallback([=](Step *step, Direction direction) {
if (direction == Direction::Back) {
historyMove(direction);
} else {
moveToStep(step, direction);
}
});
step->setShowResetCallback([this] {
step->setShowResetCallback([=] {
showResetButton();
});
step->setToggleTermsCallback([=](QString countryCode) {
toggleTerms(countryCode);
});
step->setAcceptTermsCallback([=](
QString countryCode,
base::lambda<void()> callback) {
acceptTerms(countryCode, callback);
});
}
void Widget::showResetButton() {
if (!_resetAccount) {
auto entity = object_ptr<Ui::RoundButton>(this, langFactory(lng_signin_reset_account), st::introResetButton);
_resetAccount.create(
this,
std::move(entity));
_resetAccount.create(this, std::move(entity));
_resetAccount->hide(anim::type::instant);
_resetAccount->entity()->setClickedCallback([this] { resetAccount(); });
updateControlsGeometry();
}
_resetAccount->show(anim::type::normal);
if (_changeLanguage) _changeLanguage->hide(anim::type::normal);
if (_changeLanguage) {
_changeLanguage->hide(anim::type::normal);
}
}
void Widget::toggleTerms(const QString &countryCode) {
_termsCountryCode = countryCode;
if (countryCode.isEmpty()) {
if (_terms) hideAndDestroy(std::exchange(_terms, { nullptr }));
} else {
if (!_terms) {
auto entity = object_ptr<Ui::FlatLabel>(
this,
lng_terms_signup(
lt_link,
textcmdLink(1, lang(lng_terms_signup_link))),
Ui::FlatLabel::InitType::Rich,
st::introTermsLabel);
_terms.create(this, std::move(entity));
_terms->hide(anim::type::instant);
_terms->entity()->setLink(
1,
std::make_shared<LambdaClickHandler>([=] { showTerms(); }));
updateControlsGeometry();
}
_terms->toggle(!_termsCountryCode.isEmpty(), anim::type::normal);
}
if (_changeLanguage) {
_changeLanguage->toggle(
!_terms && !_resetAccount,
anim::type::normal);
}
}
void Widget::acceptTerms(
const QString &countryCode,
base::lambda<void()> callback) {
_termsCountryCode = countryCode;
showTerms(callback);
}
void Widget::resetAccount() {
@ -308,7 +437,10 @@ void Widget::resetAccount() {
void Widget::getNearestDC() {
request(MTPhelp_GetNearestDc()).done([this](const MTPNearestDc &result) {
auto &nearest = result.c_nearestDc();
DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3").arg(qs(nearest.vcountry)).arg(nearest.vnearest_dc.v).arg(nearest.vthis_dc.v));
DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3"
).arg(qs(nearest.vcountry)
).arg(nearest.vnearest_dc.v
).arg(nearest.vthis_dc.v));
Messenger::Instance().suggestMainDcId(nearest.vnearest_dc.v);
auto nearestCountry = qs(nearest.vcountry);
if (getData()->country != nearestCountry) {
@ -318,6 +450,56 @@ void Widget::getNearestDC() {
}).send();
}
void Widget::showTerms(base::lambda<void()> callback) {
if (_termsCountryCode.isEmpty()) {
return;
}
const auto showLastTerms = [=] {
const auto box = Ui::show(Box<TermsBox>(
_termsLastText,
langFactory(callback ? lng_terms_agree : lng_box_ok)));
box->agreeClicks(
) | rpl::start_with_next([=] {
if (callback) {
callback();
}
if (box) {
box->closeBox();
}
}, box->lifetime());
};
const auto langPack = Lang::Current().id();
const auto code = _termsCountryCode;
if (_termsLastCountryCode == code && _termsLastLangPack == langPack) {
showLastTerms();
return;
}
request(MTPhelp_GetTermsOfService(
MTP_string(code)
)).done([=](const MTPhelp_TermsOfService &result) {
const auto text = qs(result.c_help_termsOfService().vtext);
const auto match = QRegularExpression("\\[([^\\]]+)\\]").match(text);
const auto linked = [&] {
if (!match.hasMatch()) {
return text;
}
const auto from = match.capturedStart(0);
const auto till = from + match.capturedLength(0);
return text.mid(0, from)
+ textcmdLink(
"http://telegram.org/privacy",
text.mid(from + 1, till - from - 2))
+ text.mid(till);
}();
_termsLastCountryCode = code;
_termsLastLangPack = langPack;
_termsLastText = linked;
showLastTerms();
}).send();
}
void Widget::showControls() {
getStep()->show();
_next->show();
@ -325,8 +507,14 @@ void Widget::showControls() {
_connecting->setForceHidden(false);
auto hasCover = getStep()->hasCover();
_settings->toggle(!hasCover, anim::type::instant);
if (_update) _update->toggle(!hasCover, anim::type::instant);
if (_changeLanguage) _changeLanguage->toggle(!_resetAccount, anim::type::instant);
if (_update) {
_update->toggle(!hasCover, anim::type::instant);
}
if (_changeLanguage) {
_changeLanguage->toggle(
!_resetAccount && !_terms,
anim::type::instant);
}
_back->toggle(getStep()->hasBack(), anim::type::instant);
}
@ -434,6 +622,9 @@ void Widget::updateControlsGeometry() {
if (_resetAccount) {
_resetAccount->moveToLeft((width() - _resetAccount->width()) / 2, height() - st::introResetBottom - _resetAccount->height());
}
if (_terms) {
_terms->moveToLeft((width() - _terms->width()) / 2, height() - st::introTermsBottom - _terms->height());
}
}
@ -833,6 +1024,17 @@ void Widget::Step::setShowResetCallback(base::lambda<void()> callback) {
_showResetCallback = std::move(callback);
}
void Widget::Step::setToggleTermsCallback(
base::lambda<void(QString countryCode)> callback) {
_toggleTermsCallback = std::move(callback);
}
void Widget::Step::setAcceptTermsCallback(base::lambda<void(
QString countryCode,
base::lambda<void()> callback)> callback) {
_acceptTermsCallback = std::move(callback);
}
void Widget::Step::showFast() {
show();
showFinished();

View File

@ -94,8 +94,14 @@ public:
setFocus();
}
void setGoCallback(base::lambda<void(Step *step, Direction direction)> callback);
void setGoCallback(
base::lambda<void(Step *step, Direction direction)> callback);
void setShowResetCallback(base::lambda<void()> callback);
void setToggleTermsCallback(
base::lambda<void(QString countryCode)> callback);
void setAcceptTermsCallback(base::lambda<void(
QString countryCode,
base::lambda<void()> callback)> callback);
void prepareShowAnimated(Step *after);
void showAnimated(Direction direction);
@ -153,6 +159,16 @@ public:
void showResetButton() {
if (_showResetCallback) _showResetCallback();
}
void toggleTerms(const QString &countryCode) {
if (_toggleTermsCallback) _toggleTermsCallback(countryCode);
}
void acceptTerms(
const QString &countryCode,
base::lambda<void()> callback) {
if (_acceptTermsCallback) {
_acceptTermsCallback(countryCode, callback);
}
}
private:
struct CoverAnimation {
@ -187,6 +203,10 @@ public:
bool _hasCover = false;
base::lambda<void(Step *step, Direction direction)> _goCallback;
base::lambda<void()> _showResetCallback;
base::lambda<void(QString countryCode)> _toggleTermsCallback;
base::lambda<void(
QString countryCode,
base::lambda<void()> callback)> _acceptTermsCallback;
object_ptr<Ui::FlatLabel> _title;
base::lambda<QString()> _titleTextFactory;
@ -224,6 +244,12 @@ private:
void showResetButton();
void resetAccount();
void toggleTerms(const QString &countryCode);
void acceptTerms(
const QString &countryCode,
base::lambda<void()> callback);
void hideAndDestroy(object_ptr<Ui::FadeWrap<Ui::RpWidget>> widget);
Step *getStep(int skip = 0) {
Assert(_stepHistory.size() + skip > 0);
return _stepHistory.at(_stepHistory.size() - skip - 1);
@ -233,6 +259,7 @@ private:
void appendStep(Step *step);
void getNearestDC();
void showTerms(base::lambda<void()> callback = nullptr);
Animation _a_show;
bool _showBack = false;
@ -253,6 +280,11 @@ private:
object_ptr<Ui::RoundButton> _next;
object_ptr<Ui::FadeWrap<Ui::LinkButton>> _changeLanguage = { nullptr };
object_ptr<Ui::FadeWrap<Ui::RoundButton>> _resetAccount = { nullptr };
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _terms = { nullptr };
QString _termsCountryCode;
QString _termsLastCountryCode;
QString _termsLastLangPack;
QString _termsLastText;
base::unique_qptr<Window::ConnectingWidget> _connecting;

View File

@ -162,11 +162,12 @@ void CountryInput::leaveEventHook(QEvent *e) {
void CountryInput::onChooseCode(const QString &code) {
Ui::hideLayer();
_chosenIso = QString();
if (code.length()) {
CountriesByCode::const_iterator i = _countriesByCode.constFind(code);
if (i != _countriesByCode.cend()) {
const CountryInfo *info = *i;
lastValidISO = info->iso2;
_chosenIso = lastValidISO = info->iso2;
setText(QString::fromUtf8(info->name));
} else {
setText(lang(lng_bad_country_code));
@ -183,8 +184,9 @@ bool CountryInput::onChooseCountry(const QString &iso) {
CountriesByISO2::const_iterator i = _countriesByISO2.constFind(iso);
const CountryInfo *info = (i == _countriesByISO2.cend()) ? 0 : (*i);
_chosenIso = QString();
if (info) {
lastValidISO = info->iso2;
_chosenIso = lastValidISO = info->iso2;
setText(QString::fromUtf8(info->name));
emit codeChanged(info->code);
update();

View File

@ -23,6 +23,10 @@ class CountryInput : public TWidget {
public:
CountryInput(QWidget *parent, const style::InputField &st);
QString iso() const {
return _chosenIso;
}
public slots:
void onChooseCode(const QString &code);
bool onChooseCountry(const QString &country);
@ -43,6 +47,7 @@ private:
const style::InputField &_st;
bool _active = false;
QString _text;
QString _chosenIso;
QPainterPath _placeholderPath;
};