2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 14:38:15 +00:00

Intro redesign done.

This commit is contained in:
John Preston
2016-11-24 22:28:23 +03:00
parent 6e0394dd42
commit 3da0533339
71 changed files with 2150 additions and 1776 deletions

View File

@@ -21,119 +21,135 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
using "basic.style";
using "ui/widgets/widgets.style";
countryInput {
width: pixels;
height: pixels;
top: pixels;
bgColor: color;
ptrSize: size;
textMrg: margins;
font: font;
align: align;
}
introCoverHeight: 208px;
introCoverTopBg: #0f89d0;
introCoverBottomBg: #39b0f0;
introCoverIconsFg: #5ec6ff;
introCoverMaxWidth: 880px;
introCoverIconsMinSkip: 120px;
introCoverLeft: icon {{ "intro_left", introCoverIconsFg }};
introCoverRight: icon {{ "intro_right", introCoverIconsFg }};
introCoverIcon: icon {
{ "intro_plane_trace", #5ec6ff69 },
{ "intro_plane_inner", #c6d8e8 },
{ "intro_plane_outer", #a1bed4 },
{ "intro_plane_top", #ffffff },
};
introCoverIconLeft: 50px;
introCoverIconTop: 46px;
introCountry: countryInput {
width: 300px;
height: 41px;
top: 33px;
bgColor: windowBgOver;
ptrSize: size(15px, 8px);
textMrg: margins(16px, 5px, 16px, 15px);
font: defaultInputFont;
align: align(left);
}
introSettingsSkip: 10px;
introIcon: icon {{ "intro_logo", #008ed5 }};
introPhotoSize: 76px;
introPhotoIconPosition: point(23px, 25px);
introPhotoTop: 10px;
introResetLink: LinkButton(defaultLinkButton) {
color: #d15948;
overColor: #d15948;
downColor: #db6352;
}
introBtnTop: 288px;
introSkip: 25px;
introFinishSkip: 15px;
introPhotoSize: 98px;
introHeaderFont: font(24px);
introHeaderSkip: 14px;
introIconSkip: 50px;
introFont: font(16px);
introLink: LinkButton(defaultLinkButton) {
font: introFont;
overFont: font(16px underline);
}
introLabel: FlatLabel(defaultFlatLabel) {
font: introFont;
introCoverTitle: FlatLabel(defaultFlatLabel) {
font: font(22px semibold);
textFg: introTitleFg;
align: align(center);
}
introCoverTitleTop: 126px;
introCoverDescription: FlatLabel(defaultFlatLabel) {
font: font(15px);
textFg: introDescriptionFg;
align: align(center);
}
introCoverDescriptionTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 24px;
}
introCoverDescriptionTop: 164px;
introTitle: FlatLabel(defaultFlatLabel) {
font: font(17px semibold);
textFg: introTitleFg;
}
introTitleTop: 11px;
introDescription: FlatLabel(defaultFlatLabel) {
font: normalFont;
textFg: introDescriptionFg;
}
introDescriptionTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 20px;
}
introDescriptionTop: 44px;
introStepSize: size(400px, 200px);
introSize: size(400px, 460px);
introSlideShift: 500px; // intro hiding animation
introLink: defaultLinkButton;
introPlaneWidth: 48px;
introPlaneHeight: 38px;
introHeight: 396px;
introStepTopMin: 86px;
introStepWidth: 380px;
introStepHeight: 256px;
introStepHeightAdd: 30px;
introStepHeightFull: 580px;
introSlideDuration: 200;
introSlideDelta: 0; // between hide start and show start
introTextTop: 22px;
introTextSize: size(400px, 93px);
introCallSkip: 15px;
introPwdTextSize: size(400px, 73px);
introCoverDuration: 200;
introNextButton: RoundButton(defaultActiveButton) {
width: 300px;
height: 56px;
textTop: 16px;
font: font(17px);
textTop: 17px;
font: font(17px semibold);
}
introPhoneTop: 8px;
introCountryCode: FlatInput(defaultFlatInput) {
width: 70px;
introStepFieldTop: 116px;
introPhoneTop: 16px;
introLinkTop: 21px;
introCountry: InputField(defaultInputField) {
textMargins: margins(3px, 7px, 3px, 6px);
font: font(16px);
width: 300px;
height: 41px;
align: align(center);
}
introPhone: FlatInput(defaultFlatInput) {
textMrg: margins(12px, 5px, 12px, 6px);
introCountryCode: InputField(introCountry) {
width: 64px;
height: 41px;
textAlign: align(top);
}
introPhone: InputField(introCountry) {
textMargins: margins(12px, 7px, 12px, 6px);
width: 225px;
height: 41px;
}
introCode: FlatInput(defaultFlatInput) {
textMrg: margins(12px, 5px, 12px, 6px);
width: 106px;
height: 41px;
align: align(center);
introCode: introCountry;
introName: introCountry;
introPassword: introCountry;
introPasswordTop: 94px;
introPasswordHintTop: 146px;
phPos: point(0px, 0px);
phAlign: align(center);
phShift: 0px;
introPasswordHint: FlatLabel(introDescription) {
textFg: windowFg;
}
introName: FlatInput(introPhone) {
width: 192px;
}
introPassword: FlatInput(introPhone) {
width: 300px;
introPasswordHintTextStyle: introDescriptionTextStyle;
introResetButton: RoundButton(defaultLightButton) {
textFg: attentionButtonFg;
textFgOver: attentionButtonFgOver;
textBgOver: attentionButtonBgOver;
ripple: RippleAnimation(defaultRippleAnimation) {
color: attentionButtonBgRipple;
}
}
introResetBottom: 20px;
introCountryIcon: icon {{ "intro_country_dropdown", menuIconFg }};
introCountryIconPosition: point(8px, 17px);
introSelectDelta: 30px;
introErrorWidth: 450px;
introErrorTop: 225px;
introErrorBelowLinkTop: 213px;
introErrorDuration: 200;
introErrorTop: 15px;
introErrorHeight: 40px;
introErrorFont: font(16px);
introLabelTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 30px;
}
introErrorLabelTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 27px;
}
introErrorLabel: FlatLabel(defaultFlatLabel) {
font: introErrorFont;
introError: introDescription;
introErrorCentered: FlatLabel(introError) {
align: align(center);
}
introErrorTextStyle: introDescriptionTextStyle;
introBackButton: IconButton(defaultIconButton) {
width: 56px;
height: 56px;

View File

@@ -26,21 +26,28 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "intro/introsignup.h"
#include "intro/intropwdcheck.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "styles/style_intro.h"
CodeInput::CodeInput(QWidget *parent, const style::FlatInput &st, const QString &ph) : Ui::FlatInput(parent, st, ph) {
namespace Intro {
CodeInput::CodeInput(QWidget *parent, const style::InputField &st, const QString &ph) : Ui::MaskedInputField(parent, st, ph) {
}
void CodeInput::correctValue(const QString &was, QString &now) {
void CodeInput::setDigitsCountMax(int digitsCount) {
_digitsCountMax = digitsCount;
}
void CodeInput::correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) {
QString newText;
int oldPos(cursorPosition()), newPos(-1), oldLen(now.length()), digitCount = 0;
int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = 0;
for (int i = 0; i < oldLen; ++i) {
if (now[i].isDigit()) {
++digitCount;
}
}
if (digitCount > 5) digitCount = 5;
bool strict = (digitCount == 5);
accumulate_min(digitCount, _digitsCountMax);
auto strict = (digitCount == _digitsCountMax);
newText.reserve(oldLen);
for (int i = 0; i < oldLen; ++i) {
@@ -58,177 +65,131 @@ void CodeInput::correctValue(const QString &was, QString &now) {
newPos = newText.length();
}
}
if (newPos < 0) {
newPos = newText.length();
if (newPos < 0 || newPos > newText.size()) {
newPos = newText.size();
}
if (newText != now) {
now = newText;
setText(now);
updatePlaceholder();
if (newPos != oldPos) {
setCursorPosition(newPos);
}
}
if (newPos != nowCursor) {
nowCursor = newPos;
setCursorPosition(nowCursor);
}
if (strict) emit codeEntered();
}
IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, _a_error(animation(this, &IntroCode::step_error))
, _next(this, lang(lng_intro_next), st::introNextButton)
, _desc(st::introTextSize.width())
CodeWidget::CodeWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink)
, _noTelegramCodeRequestId(0)
, _code(this, st::introCode, lang(lng_code_ph))
, _callTimer(this)
, _callStatus(intro()->getCallStatus())
, _callStatus(getData()->callStatus)
, _callTimeout(getData()->callTimeout)
, _callLabel(this, st::introDescription, st::introDescriptionTextStyle)
, _checkRequest(this) {
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitCode()));
connect(_code, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_callTimer, SIGNAL(timeout()), this, SLOT(onSendCall()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
connect(_noTelegramCode, SIGNAL(clicked()), this, SLOT(onNoTelegramCode()));
updateDescText();
_code->setDigitsCountMax(getData()->codeLength);
setErrorBelowLink(true);
if (!intro()->codeByTelegram()) {
if (_callStatus.type == IntroWidget::CallWaiting) {
_callTimer->start(1000);
}
}
setTitleText(App::formatPhone(getData()->phone));
updateDescText();
}
void IntroCode::updateDescText() {
_desc.setRichText(st::introFont, lang(intro()->codeByTelegram() ? lng_code_telegram : lng_code_desc));
if (intro()->codeByTelegram()) {
void CodeWidget::updateDescText() {
setDescriptionText(lang(getData()->codeByTelegram ? lng_code_telegram : lng_code_desc));
if (getData()->codeByTelegram) {
_noTelegramCode->show();
_callTimer->stop();
} else {
_noTelegramCode->hide();
_callStatus = intro()->getCallStatus();
if (_callStatus.type == IntroWidget::CallWaiting && !_callTimer->isActive()) {
_callStatus = getData()->callStatus;
_callTimeout = getData()->callTimeout;
if (_callStatus == Widget::Data::CallStatus::Waiting && !_callTimer->isActive()) {
_callTimer->start(1000);
}
}
update();
updateCallText();
}
void IntroCode::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
bool codeByTelegram = intro()->codeByTelegram();
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, intro()->getPhone(), style::al_top);
p.setFont(st::introFont->f);
_desc.draw(p, _textRect.x(), _textRect.y() + _textRect.height() - 2 * st::introFont->height, _textRect.width(), style::al_top);
}
if (codeByTelegram) {
} else {
QString callText;
switch (_callStatus.type) {
case IntroWidget::CallWaiting: {
if (_callStatus.timeout >= 3600) {
callText = lng_code_call(lt_minutes, qsl("%1:%2").arg(_callStatus.timeout / 3600).arg((_callStatus.timeout / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(_callStatus.timeout % 60, 2, 10, QChar('0')));
void CodeWidget::updateCallText() {
auto text = ([this]() -> QString {
if (getData()->codeByTelegram) {
return QString();
}
switch (_callStatus) {
case Widget::Data::CallStatus::Waiting: {
if (_callTimeout >= 3600) {
return lng_code_call(lt_minutes, qsl("%1:%2").arg(_callTimeout / 3600).arg((_callTimeout / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(_callTimeout % 60, 2, 10, QChar('0')));
} else {
callText = lng_code_call(lt_minutes, QString::number(_callStatus.timeout / 60), lt_seconds, qsl("%1").arg(_callStatus.timeout % 60, 2, 10, QChar('0')));
return lng_code_call(lt_minutes, QString::number(_callTimeout / 60), lt_seconds, qsl("%1").arg(_callTimeout % 60, 2, 10, QChar('0')));
}
} break;
case IntroWidget::CallCalling: {
callText = lang(lng_code_calling);
} break;
case IntroWidget::CallCalled: {
callText = lang(lng_code_called);
} break;
case Widget::Data::CallStatus::Calling: return lang(lng_code_calling);
case Widget::Data::CallStatus::Called: return lang(lng_code_called);
}
if (!callText.isEmpty()) {
p.drawText(QRect(_textRect.left(), _code->y() + _code->height() + st::introCallSkip, st::introTextSize.width(), st::introErrorHeight), callText, style::al_center);
}
}
if (_a_error.animating() || _error.length()) {
p.setOpacity(a_errorAlpha.current());
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(QRect(_textRect.left(), _next->y() + _next->height() + st::introErrorTop, st::introTextSize.width(), st::introErrorHeight), _error, style::al_center);
}
return QString();
})();
_callLabel->setText(text);
_callLabel->setVisible(!text.isEmpty() && !animating());
}
void IntroCode::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_code->move((width() - _code->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
}
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
_noTelegramCode->move(_textRect.left() + (st::introTextSize.width() - _noTelegramCode->width()) / 2, _code->y() + _code->height() + st::introCallSkip + (st::introErrorHeight - _noTelegramCode->height()) / 2);
void CodeWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_code->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
auto linkTop = _code->y() + _code->height() + st::introLinkTop;
_noTelegramCode->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
_callLabel->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
}
void IntroCode::showError(const QString &error) {
if (!error.isEmpty()) _code->notaBene();
if (!_a_error.animating() && error == _error) return;
if (error.length()) {
_error = error;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_a_error.start();
void CodeWidget::showCodeError(const QString &text) {
if (!text.isEmpty()) _code->showError();
showError(text);
}
void IntroCode::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
_error.clear();
}
} else {
a_errorAlpha.update(dt, anim::linear);
}
if (timer) update();
}
void IntroCode::activate() {
IntroStep::activate();
void CodeWidget::setInnerFocus() {
_code->setFocus();
}
void IntroCode::finished() {
IntroStep::finished();
_error.clear();
a_errorAlpha = anim::fvalue(0);
_sentCode.clear();
_code->setDisabled(false);
_callTimer->stop();
_code->setText(QString());
rpcClear();
}
void IntroCode::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
void CodeWidget::activate() {
Step::activate();
_code->show();
if (getData()->codeByTelegram) {
_noTelegramCode->show();
} else {
_callLabel->show();
}
MTP::send(MTPauth_CancelCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())));
setInnerFocus();
}
void IntroCode::stopCheck() {
void CodeWidget::finished() {
Step::finished();
_checkRequest->stop();
_callTimer->stop();
rpcClear();
cancelled();
_sentCode.clear();
_code->setText(QString());
_code->setDisabled(false);
}
void CodeWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
MTP::cancel(base::take(_callRequestId));
MTP::send(MTPauth_CancelCode(MTP_string(getData()->phone), MTP_string(getData()->phoneHash)));
}
void CodeWidget::stopCheck() {
_checkRequest->stop();
}
void IntroCode::onCheckRequest() {
void CodeWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@@ -248,26 +209,25 @@ void IntroCode::onCheckRequest() {
}
}
void IntroCode::codeSubmitDone(const MTPauth_Authorization &result) {
void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) {
stopCheck();
_sentRequest = 0;
_code->setDisabled(false);
const auto &d(result.c_auth_authorization());
auto &d = result.c_auth_authorization();
if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf?
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
return;
}
cSetLoggedPhoneNumber(intro()->getPhone());
intro()->finish(d.vuser);
cSetLoggedPhoneNumber(getData()->phone);
finish(d.vuser);
}
bool IntroCode::codeSubmitFail(const RPCError &error) {
bool CodeWidget::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
stopCheck();
_sentRequest = 0;
showError(lang(lng_flood_error));
_code->setDisabled(false);
_code->setFocus();
showCodeError(lang(lng_flood_error));
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
@@ -277,138 +237,138 @@ bool IntroCode::codeSubmitFail(const RPCError &error) {
_code->setDisabled(false);
const QString &err = error.type();
if (err == qstr("PHONE_NUMBER_INVALID") || err == qstr("PHONE_CODE_EXPIRED")) { // show error
intro()->onBack();
goBack();
return true;
} else if (err == qstr("PHONE_CODE_EMPTY") || err == qstr("PHONE_CODE_INVALID")) {
showError(lang(lng_bad_code));
_code->notaBene();
showCodeError(lang(lng_bad_code));
return true;
} else if (err == qstr("PHONE_NUMBER_UNOCCUPIED")) { // success, need to signUp
intro()->setCode(_sentCode);
intro()->replaceStep(new IntroSignup(intro()));
getData()->code = _sentCode;
goReplace(new Intro::SignupWidget(parentWidget(), getData()));
return true;
} else if (err == qstr("SESSION_PASSWORD_NEEDED")) {
intro()->setCode(_sentCode);
getData()->code = _sentCode;
_code->setDisabled(false);
_checkRequest->start(1000);
_sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&IntroCode::gotPassword), rpcFail(&IntroCode::codeSubmitFail));
_sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&CodeWidget::gotPassword), rpcFail(&CodeWidget::codeSubmitFail));
return true;
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
showCodeError(err + ": " + error.description());
} else {
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
}
_code->setFocus();
return false;
}
void IntroCode::onInputChange() {
showError(QString());
if (_code->text().length() == 5) onSubmitCode();
void CodeWidget::onInputChange() {
hideError();
if (_code->getLastText().length() == getData()->codeLength) {
submit();
}
}
void IntroCode::onSendCall() {
if (_callStatus.type == IntroWidget::CallWaiting) {
if (--_callStatus.timeout <= 0) {
_callStatus.type = IntroWidget::CallCalling;
void CodeWidget::onSendCall() {
if (_callStatus == Widget::Data::CallStatus::Waiting) {
if (--_callTimeout <= 0) {
_callStatus = Widget::Data::CallStatus::Calling;
_callTimer->stop();
MTP::send(MTPauth_ResendCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())), rpcDone(&IntroCode::callDone));
_callRequestId = MTP::send(MTPauth_ResendCode(MTP_string(getData()->phone), MTP_string(getData()->phoneHash)), rpcDone(&CodeWidget::callDone));
} else {
intro()->setCallStatus(_callStatus);
getData()->callStatus = _callStatus;
getData()->callTimeout = _callTimeout;
}
}
update();
}
void IntroCode::callDone(const MTPauth_SentCode &v) {
if (_callStatus.type == IntroWidget::CallCalling) {
_callStatus.type = IntroWidget::CallCalled;
intro()->setCallStatus(_callStatus);
update();
updateCallText();
}
}
void IntroCode::gotPassword(const MTPaccount_Password &result) {
void CodeWidget::callDone(const MTPauth_SentCode &v) {
if (v.type() == mtpc_auth_sentCode) {
fillSentCodeData(v.c_auth_sentCode().vtype);
_code->setDigitsCountMax(getData()->codeLength);
}
if (_callStatus == Widget::Data::CallStatus::Calling) {
_callStatus = Widget::Data::CallStatus::Called;
getData()->callStatus = _callStatus;
getData()->callTimeout = _callTimeout;
updateCallText();
}
}
void CodeWidget::gotPassword(const MTPaccount_Password &result) {
stopCheck();
_sentRequest = 0;
_code->setDisabled(false);
switch (result.type()) {
case mtpc_account_noPassword: // should not happen
case mtpc_account_noPassword: { // should not happen
_code->setFocus();
break;
} break;
case mtpc_account_password: {
const auto &d(result.c_account_password());
intro()->setPwdSalt(qba(d.vcurrent_salt));
intro()->setHasRecovery(mtpIsTrue(d.vhas_recovery));
intro()->setPwdHint(qs(d.vhint));
intro()->replaceStep(new IntroPwdCheck(intro()));
auto &d = result.c_account_password();
getData()->pwdSalt = qba(d.vcurrent_salt);
getData()->hasRecovery = mtpIsTrue(d.vhas_recovery);
getData()->pwdHint = qs(d.vhint);
goReplace(new Intro::PwdCheckWidget(parentWidget(), getData()));
} break;
}
}
void IntroCode::onSubmitCode() {
void CodeWidget::submit() {
if (_sentRequest) return;
_code->setDisabled(true);
setFocus();
showError(QString());
hideError();
_checkRequest->start(1000);
_sentCode = _code->text();
intro()->setPwdSalt(QByteArray());
intro()->setHasRecovery(false);
intro()->setPwdHint(QString());
_sentRequest = MTP::send(MTPauth_SignIn(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(_sentCode)), rpcDone(&IntroCode::codeSubmitDone), rpcFail(&IntroCode::codeSubmitFail));
_sentCode = _code->getLastText();
getData()->pwdSalt = QByteArray();
getData()->hasRecovery = false;
getData()->pwdHint = QString();
_sentRequest = MTP::send(MTPauth_SignIn(MTP_string(getData()->phone), MTP_string(getData()->phoneHash), MTP_string(_sentCode)), rpcDone(&CodeWidget::codeSubmitDone), rpcFail(&CodeWidget::codeSubmitFail));
}
void IntroCode::onNoTelegramCode() {
void CodeWidget::onNoTelegramCode() {
if (_noTelegramCodeRequestId) return;
_noTelegramCodeRequestId = MTP::send(MTPauth_ResendCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())), rpcDone(&IntroCode::noTelegramCodeDone), rpcFail(&IntroCode::noTelegramCodeFail));
_noTelegramCodeRequestId = MTP::send(MTPauth_ResendCode(MTP_string(getData()->phone), MTP_string(getData()->phoneHash)), rpcDone(&CodeWidget::noTelegramCodeDone), rpcFail(&CodeWidget::noTelegramCodeFail));
}
void IntroCode::noTelegramCodeDone(const MTPauth_SentCode &result) {
void CodeWidget::noTelegramCodeDone(const MTPauth_SentCode &result) {
if (result.type() != mtpc_auth_sentCode) {
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
return;
}
const auto &d(result.c_auth_sentCode());
switch (d.vtype.type()) {
case mtpc_auth_sentCodeTypeApp: intro()->setCodeByTelegram(true);
case mtpc_auth_sentCodeTypeSms:
case mtpc_auth_sentCodeTypeCall: intro()->setCodeByTelegram(false);
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
auto &d = result.c_auth_sentCode();
fillSentCodeData(d.vtype);
_code->setDigitsCountMax(getData()->codeLength);
if (d.has_next_type() && d.vnext_type.type() == mtpc_auth_codeTypeCall) {
intro()->setCallStatus({ IntroWidget::CallWaiting, d.has_timeout() ? d.vtimeout.v : 60 });
getData()->callStatus = Widget::Data::CallStatus::Waiting;
getData()->callTimeout = d.has_timeout() ? d.vtimeout.v : 60;
} else {
intro()->setCallStatus({ IntroWidget::CallDisabled, 0 });
getData()->callStatus = Widget::Data::CallStatus::Disabled;
getData()->callTimeout = 0;
}
intro()->setCodeByTelegram(false);
getData()->codeByTelegram = false;
updateDescText();
}
bool IntroCode::noTelegramCodeFail(const RPCError &error) {
bool CodeWidget::noTelegramCodeFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
showError(lang(lng_flood_error));
_code->setFocus();
showCodeError(lang(lng_flood_error));
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
if (cDebug()) { // internal server error
showError(error.type() + ": " + error.description());
showCodeError(error.type() + ": " + error.description());
} else {
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
}
_code->setFocus();
return false;
}
void IntroCode::onSubmit() {
onSubmitCode();
}
} // namespace Intro

View File

@@ -26,55 +26,63 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class RoundButton;
class LinkButton;
class FlatLabel;
} // namespace Ui
class CodeInput final : public Ui::FlatInput {
namespace Intro {
class CodeInput final : public Ui::MaskedInputField {
Q_OBJECT
public:
CodeInput(QWidget *parent, const style::FlatInput &st, const QString &ph);
CodeInput(QWidget *parent, const style::InputField &st, const QString &ph);
void setDigitsCountMax(int digitsCount);
signals:
void codeEntered();
protected:
void correctValue(const QString &was, QString &now);
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
private:
int _digitsCountMax = 5;
};
class IntroCode final : public IntroStep {
class CodeWidget : public Widget::Step {
Q_OBJECT
public:
IntroCode(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void step_error(float64 ms, bool timer);
CodeWidget(QWidget *parent, Widget::Data *data);
bool hasBack() const override {
return true;
}
void setInnerFocus() override;
void activate() override;
void finished() override;
void cancelled() override;
void onSubmit() override;
void codeSubmitDone(const MTPauth_Authorization &result);
bool codeSubmitFail(const RPCError &error);
void submit() override;
void updateDescText();
public slots:
void onSubmitCode();
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onNoTelegramCode();
void onInputChange();
void onSendCall();
void onCheckRequest();
private:
void showError(const QString &err);
void updateCallText();
void codeSubmitDone(const MTPauth_Authorization &result);
bool codeSubmitFail(const RPCError &error);
void showCodeError(const QString &text);
void callDone(const MTPauth_SentCode &v);
void gotPassword(const MTPaccount_Password &result);
@@ -83,23 +91,21 @@ private:
void stopCheck();
QString _error;
anim::fvalue a_errorAlpha;
Animation _a_error;
ChildWidget<Ui::RoundButton> _next;
Text _desc;
ChildWidget<Ui::LinkButton> _noTelegramCode;
mtpRequestId _noTelegramCodeRequestId;
QRect _textRect;
mtpRequestId _noTelegramCodeRequestId = 0;
ChildWidget<CodeInput> _code;
QString _sentCode;
mtpRequestId _sentRequest = 0;
ChildObject<QTimer> _callTimer;
IntroWidget::CallStatus _callStatus;
Widget::Data::CallStatus _callStatus;
int _callTimeout;
mtpRequestId _callRequestId = 0;
ChildWidget<Ui::FlatLabel> _callLabel;
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@@ -27,20 +27,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_intro.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/effects/widget_fade_wrap.h"
#include "core/click_handler_types.h"
IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, _a_error(animation(this, &IntroPhone::step_error))
, _next(this, lang(lng_intro_next), st::introNextButton)
namespace Intro {
PhoneWidget::PhoneWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _country(this, st::introCountry)
, _phone(this, st::introPhone)
, _code(this, st::introCountryCode)
, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), Ui::FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle)
, _phone(this, st::introPhone)
, _checkRequest(this) {
setVisible(false);
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPhone()));
connect(_phone, SIGNAL(voidBackspace(QKeyEvent*)), _code, SLOT(startErasing(QKeyEvent*)));
connect(_country, SIGNAL(codeChanged(const QString &)), _code, SLOT(codeSelected(const QString &)));
connect(_code, SIGNAL(codeChanged(const QString &)), _country, SLOT(onChooseCode(const QString &)));
@@ -49,146 +45,112 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent)
connect(_code, SIGNAL(addedToNumber(const QString &)), _phone, SLOT(addedToNumber(const QString &)));
connect(_phone, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_code, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(intro(), SIGNAL(countryChanged()), this, SLOT(countryChanged()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
_signup->setLink(1, MakeShared<LambdaClickHandler>([this] {
toSignUp();
}));
_signup->hide();
setTitleText(lang(lng_phone_title));
setDescriptionText(lang(lng_phone_desc));
subscribe(getData()->updated, [this] { countryChanged(); });
setErrorCentered(true);
_signupCache = myGrab(_signup);
if (!_country->onChooseCountry(intro()->currentCountry())) {
if (!_country->onChooseCountry(getData()->country)) {
_country->onChooseCountry(qsl("US"));
}
_changed = false;
}
void IntroPhone::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
void PhoneWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_country->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
auto phoneTop = _country->y() + _country->height() + st::introPhoneTop;
_code->moveToLeft(contentLeft(), phoneTop);
_phone->moveToLeft(contentLeft() + _country->width() - st::introPhone.width, phoneTop);
updateSignupGeometry();
}
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, lang(lng_phone_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(_textRect, lang(lng_phone_desc), style::al_bottom);
}
if (_a_error.animating() || _error.length()) {
int32 errorY = _showSignup ? ((_phone->y() + _phone->height() + _next->y() - st::introErrorFont->height) / 2) : (_next->y() + _next->height() + st::introErrorTop);
p.setOpacity(a_errorAlpha.current());
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(QRect(_textRect.x(), errorY, _textRect.width(), st::introErrorFont->height), _error, style::al_top);
if (_signup->isHidden() && _showSignup) {
p.drawPixmap(_signup->x(), _signup->y(), _signupCache);
}
void PhoneWidget::updateSignupGeometry() {
if (_signup) {
_signup->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introDescriptionTop);
}
}
void IntroPhone::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_country->move((width() - _country->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
int phoneTop = _country->y() + _country->height() + st::introPhoneTop;
_phone->move((width() - _country->width()) / 2 + _country->width() - st::introPhone.width, phoneTop);
_code->move((width() - _country->width()) / 2, phoneTop);
}
_signup->move((width() - _signup->width()) / 2, _next->y() + _next->height() + st::introErrorTop - ((st::introErrorLabelTextStyle.lineHeight - st::introErrorFont->height) / 2));
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
void PhoneWidget::showPhoneError(const QString &text) {
_phone->showError();
showError(text);
}
void IntroPhone::showError(const QString &error, bool signUp) {
if (!error.isEmpty()) {
_phone->notaBene();
_showSignup = signUp;
void PhoneWidget::hidePhoneError() {
hideError();
if (_signup) {
_signup->fadeOut();
showDescription();
}
if (!_a_error.animating() && error == _error) return;
if (error.length()) {
_error = error;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_signup->hide();
_a_error.start();
}
void IntroPhone::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
_error.clear();
_signup->hide();
} else if (!_error.isEmpty() && _showSignup) {
_signup->show();
}
} else {
a_errorAlpha.update(dt, anim::linear);
void PhoneWidget::showSignup() {
showPhoneError(lang(lng_bad_phone_noreg));
if (!_signup) {
auto signupText = lng_phone_notreg(lt_link_start, textcmdStartLink(1), lt_link_end, textcmdStopLink(), lt_signup_start, textcmdStartLink(2), lt_signup_end, textcmdStopLink());
auto inner = new Ui::FlatLabel(this, signupText, Ui::FlatLabel::InitType::Rich, st::introDescription, st::introDescriptionTextStyle);
_signup.create(this, inner, base::lambda<void()>(), st::introErrorDuration);
_signup->entity()->setLink(1, MakeShared<UrlClickHandler>(qsl("https://telegram.org"), false));
_signup->entity()->setLink(2, MakeShared<LambdaClickHandler>([this] {
toSignUp();
}));
_signup->hideFast();
updateSignupGeometry();
}
if (timer) update();
_signup->fadeIn();
hideDescription();
}
void IntroPhone::countryChanged() {
void PhoneWidget::countryChanged() {
if (!_changed) {
selectCountry(intro()->currentCountry());
selectCountry(getData()->country);
}
}
void IntroPhone::onInputChange() {
void PhoneWidget::onInputChange() {
_changed = true;
showError(QString());
hidePhoneError();
}
void IntroPhone::disableAll() {
_next->setDisabled(true);
void PhoneWidget::disableAll() {
_phone->setDisabled(true);
_country->setDisabled(true);
_code->setDisabled(true);
setFocus();
}
void IntroPhone::enableAll(bool failed) {
_next->setDisabled(false);
void PhoneWidget::enableAll(bool failed) {
_phone->setDisabled(false);
_country->setDisabled(false);
_code->setDisabled(false);
if (failed) _phone->setFocus();
}
void IntroPhone::onSubmitPhone() {
void PhoneWidget::submit() {
if (_sentRequest || isHidden()) return;
if (!App::isValidPhone(fullNumber())) {
showError(lang(lng_bad_phone));
showPhoneError(lang(lng_bad_phone));
_phone->setFocus();
return;
}
disableAll();
showError(QString());
hidePhoneError();
_checkRequest->start(1000);
_sentPhone = fullNumber();
_sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&IntroPhone::phoneCheckDone), rpcFail(&IntroPhone::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&PhoneWidget::phoneCheckDone), rpcFail(&PhoneWidget::phoneSubmitFail));
}
void IntroPhone::stopCheck() {
void PhoneWidget::stopCheck() {
_checkRequest->stop();
}
void IntroPhone::onCheckRequest() {
void PhoneWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@@ -202,66 +164,65 @@ void IntroPhone::onCheckRequest() {
}
}
void IntroPhone::phoneCheckDone(const MTPauth_CheckedPhone &result) {
void PhoneWidget::phoneCheckDone(const MTPauth_CheckedPhone &result) {
stopCheck();
const auto &d(result.c_auth_checkedPhone());
auto &d = result.c_auth_checkedPhone();
if (mtpIsTrue(d.vphone_registered)) {
disableAll();
showError(QString());
hidePhoneError();
_checkRequest->start(1000);
MTPauth_SendCode::Flags flags = 0;
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&PhoneWidget::phoneSubmitDone), rpcFail(&PhoneWidget::phoneSubmitFail));
} else {
showError(lang(lng_bad_phone_noreg), true);
showSignup();
enableAll(true);
_sentRequest = 0;
}
}
void IntroPhone::phoneSubmitDone(const MTPauth_SentCode &result) {
void PhoneWidget::phoneSubmitDone(const MTPauth_SentCode &result) {
stopCheck();
_sentRequest = 0;
enableAll(true);
if (result.type() != mtpc_auth_sentCode) {
showError(lang(lng_server_error));
showPhoneError(lang(lng_server_error));
return;
}
const auto &d(result.c_auth_sentCode());
switch (d.vtype.type()) {
case mtpc_auth_sentCodeTypeApp: intro()->setCodeByTelegram(true); break;
case mtpc_auth_sentCodeTypeSms:
case mtpc_auth_sentCodeTypeCall: intro()->setCodeByTelegram(false); break;
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
intro()->setPhone(_sentPhone, d.vphone_code_hash.c_string().v.c_str(), d.is_phone_registered());
auto &d = result.c_auth_sentCode();
fillSentCodeData(d.vtype);
getData()->phone = _sentPhone;
getData()->phoneHash = d.vphone_code_hash.c_string().v.c_str();
getData()->phoneIsRegistered = d.is_phone_registered();
if (d.has_next_type() && d.vnext_type.type() == mtpc_auth_codeTypeCall) {
intro()->setCallStatus({ IntroWidget::CallWaiting, d.has_timeout() ? d.vtimeout.v : 60 });
getData()->callStatus = Widget::Data::CallStatus::Waiting;
getData()->callTimeout = d.has_timeout() ? d.vtimeout.v : 60;
} else {
intro()->setCallStatus({ IntroWidget::CallDisabled, 0 });
getData()->callStatus = Widget::Data::CallStatus::Disabled;
getData()->callTimeout = 0;
}
intro()->nextStep(new IntroCode(intro()));
goNext(new Intro::CodeWidget(parentWidget(), getData()));
}
void IntroPhone::toSignUp() {
void PhoneWidget::toSignUp() {
disableAll();
showError(QString());
hideError(); // Hide error, but leave the signup label visible.
_checkRequest->start(1000);
MTPauth_SendCode::Flags flags = 0;
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&PhoneWidget::phoneSubmitDone), rpcFail(&PhoneWidget::phoneSubmitFail));
}
bool IntroPhone::phoneSubmitFail(const RPCError &error) {
bool PhoneWidget::phoneSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
stopCheck();
_sentRequest = 0;
showError(lang(lng_flood_error));
showPhoneError(lang(lng_flood_error));
enableAll(true);
return true;
}
@@ -271,48 +232,50 @@ bool IntroPhone::phoneSubmitFail(const RPCError &error) {
_sentRequest = 0;
const QString &err = error.type();
if (err == qstr("PHONE_NUMBER_INVALID")) { // show error
showError(lang(lng_bad_phone));
showPhoneError(lang(lng_bad_phone));
enableAll(true);
return true;
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
showPhoneError(err + ": " + error.description());
} else {
showError(lang(lng_server_error));
showPhoneError(lang(lng_server_error));
}
enableAll(true);
return false;
}
QString IntroPhone::fullNumber() const {
return _code->text() + _phone->text();
QString PhoneWidget::fullNumber() const {
return _code->getLastText() + _phone->getLastText();
}
void IntroPhone::selectCountry(const QString &c) {
void PhoneWidget::selectCountry(const QString &c) {
_country->onChooseCountry(c);
}
void IntroPhone::activate() {
IntroStep::activate();
void PhoneWidget::setInnerFocus() {
_phone->setFocus();
}
void IntroPhone::finished() {
IntroStep::finished();
void PhoneWidget::activate() {
Step::activate();
_country->show();
_phone->show();
_code->show();
setInnerFocus();
}
void PhoneWidget::finished() {
Step::finished();
_checkRequest->stop();
rpcClear();
_error.clear();
a_errorAlpha = anim::fvalue(0);
cancelled();
enableAll(true);
}
void IntroPhone::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
void PhoneWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
}
void IntroPhone::onSubmit() {
onSubmitPhone();
}
} // namespace Intro

View File

@@ -30,35 +30,41 @@ class RoundButton;
class FlatLabel;
} // namespace Ui
class IntroPhone final : public IntroStep {
namespace Intro {
class PhoneWidget : public Widget::Step, private base::Subscriber {
Q_OBJECT
public:
IntroPhone(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void step_error(float64 ms, bool timer);
PhoneWidget(QWidget *parent, Widget::Data *data);
void selectCountry(const QString &country);
void setInnerFocus() override;
void activate() override;
void finished() override;
void cancelled() override;
void onSubmit() override;
void submit() override;
bool hasBack() const override {
return true;
}
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onInputChange();
void onCheckRequest();
private:
void updateSignupGeometry();
void countryChanged();
void phoneCheckDone(const MTPauth_CheckedPhone &result);
void phoneSubmitDone(const MTPauth_SentCode &result);
bool phoneSubmitFail(const RPCError &error);
public slots:
void countryChanged();
void onInputChange();
void onSubmitPhone();
void onCheckRequest();
private:
void toSignUp();
QString fullNumber() const;
@@ -66,24 +72,17 @@ private:
void enableAll(bool failed);
void stopCheck();
void showError(const QString &err, bool signUp = false);
QString _error;
anim::fvalue a_errorAlpha;
Animation _a_error;
void showPhoneError(const QString &text);
void hidePhoneError();
void showSignup();
bool _changed = false;
ChildWidget<Ui::RoundButton> _next;
QRect _textRect;
ChildWidget<CountryInput> _country;
ChildWidget<Ui::PhonePartInput> _phone;
ChildWidget<Ui::CountryCodeInput> _code;
ChildWidget<Ui::PhonePartInput> _phone;
ChildWidget<Ui::FlatLabel> _signup;
QPixmap _signupCache;
bool _showSignup = false;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _signup = { nullptr };
QString _sentPhone;
mtpRequestId _sentRequest = 0;
@@ -91,3 +90,5 @@ private:
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@@ -30,117 +30,52 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "intro/introsignup.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, _a_error(animation(this, &IntroPwdCheck::step_error))
, _next(this, lang(lng_intro_submit), st::introNextButton)
, _salt(parent->getPwdSalt())
, _hasRecovery(parent->getHasRecovery())
, _hint(parent->getPwdHint())
namespace Intro {
PwdCheckWidget::PwdCheckWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _salt(getData()->pwdSalt)
, _hasRecovery(getData()->hasRecovery)
, _hint(getData()->pwdHint)
, _pwdField(this, st::introPassword, lang(lng_signin_password))
, _pwdHint(this, st::introPasswordHint, st::introPasswordHintTextStyle)
, _codeField(this, st::introPassword, lang(lng_signin_code))
, _toRecover(this, lang(lng_signin_recover))
, _toPassword(this, lang(lng_signin_try_password))
, _reset(this, lang(lng_signin_reset_account), st::introResetLink)
, _checkRequest(this) {
setVisible(false);
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPwd()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
connect(_toRecover, SIGNAL(clicked()), this, SLOT(onToRecover()));
connect(_toPassword, SIGNAL(clicked()), this, SLOT(onToPassword()));
connect(_pwdField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_codeField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_reset, SIGNAL(clicked()), this, SLOT(onReset()));
_pwdField->setEchoMode(QLineEdit::Password);
setTitleText(lang(lng_signin_title));
updateDescriptionText();
setErrorBelowLink(true);
if (!_hint.isEmpty()) {
_hintText.setText(st::introFont, lng_signin_hint(lt_password_hint, _hint));
if (_hint.isEmpty()) {
_pwdHint->hide();
} else {
_pwdHint->setText(lng_signin_hint(lt_password_hint, _hint));
}
_codeField->hide();
_toPassword->hide();
_toRecover->show();
_reset->hide();
setMouseTracking(true);
}
void IntroPwdCheck::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, lang(lng_signin_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(_textRect, lang(_pwdField->isHidden() ? lng_signin_recover_desc : lng_signin_desc), style::al_bottom);
}
if (_pwdField->isHidden()) {
if (!_emailPattern.isEmpty()) {
p.drawText(QRect(_textRect.x(), _pwdField->y() + _pwdField->height() + st::introFinishSkip, _textRect.width(), st::introFont->height), _emailPattern, style::al_top);
}
} else if (!_hint.isEmpty()) {
_hintText.drawElided(p, _pwdField->x(), _pwdField->y() + _pwdField->height() + st::introFinishSkip, _pwdField->width(), 1, style::al_top);
}
if (_a_error.animating() || _error.length()) {
p.setOpacity(a_errorAlpha.current());
QRect errRect((width() - st::introErrorWidth) / 2, (_pwdField->y() + _pwdField->height() + st::introFinishSkip + st::introFont->height + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight);
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(errRect, _error, QTextOption(style::al_center));
p.setOpacity(1);
}
void PwdCheckWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_pwdField->moveToLeft(contentLeft(), contentTop() + st::introPasswordTop);
_pwdHint->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introPasswordHintTop);
_codeField->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
auto linkTop = _codeField->y() + _codeField->height() + st::introLinkTop;
_toRecover->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
_toPassword->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
}
void IntroPwdCheck::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_pwdField->move((width() - _pwdField->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_codeField->move((width() - _codeField->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_toRecover->move(_next->x() + (_pwdField->width() - _toRecover->width()) / 2, _next->y() + _next->height() + st::introFinishSkip);
_toPassword->move(_next->x() + (_pwdField->width() - _toPassword->width()) / 2, _next->y() + _next->height() + st::introFinishSkip);
_reset->move((width() - _reset->width()) / 2, _toRecover->y() + _toRecover->height() + st::introFinishSkip);
}
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
}
void IntroPwdCheck::showError(const QString &error) {
if (!_a_error.animating() && error == _error) return;
if (error.length()) {
_error = error;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_a_error.start();
}
void IntroPwdCheck::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
_error.clear();
}
} else {
a_errorAlpha.update(dt, anim::linear);
}
if (timer) update();
}
void IntroPwdCheck::activate() {
IntroStep::activate();
void PwdCheckWidget::setInnerFocus() {
if (_pwdField->isHidden()) {
_codeField->setFocus();
} else {
@@ -148,17 +83,25 @@ void IntroPwdCheck::activate() {
}
}
void IntroPwdCheck::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
void PwdCheckWidget::activate() {
if (_pwdField->isHidden() && _codeField->isHidden()) {
Step::activate();
_pwdField->show();
_pwdHint->show();
_toRecover->show();
}
setInnerFocus();
}
void IntroPwdCheck::stopCheck() {
void PwdCheckWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
}
void PwdCheckWidget::stopCheck() {
_checkRequest->stop();
}
void IntroPwdCheck::onCheckRequest() {
void PwdCheckWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@@ -176,7 +119,7 @@ void IntroPwdCheck::onCheckRequest() {
}
}
void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
_sentRequest = 0;
stopCheck();
if (recover) {
@@ -184,22 +127,22 @@ void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &res
}
_pwdField->setDisabled(false);
_codeField->setDisabled(false);
const auto &d(result.c_auth_authorization());
auto &d = result.c_auth_authorization();
if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf?
showError(lang(lng_server_error));
return;
}
intro()->finish(d.vuser);
finish(d.vuser);
}
bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
_sentRequest = 0;
stopCheck();
_codeField->setDisabled(false);
showError(lang(lng_flood_error));
_pwdField->setDisabled(false);
_pwdField->notaBene();
_pwdField->showError();
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
@@ -212,10 +155,10 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
if (err == qstr("PASSWORD_HASH_INVALID")) {
showError(lang(lng_signin_bad_password));
_pwdField->selectAll();
_pwdField->notaBene();
_pwdField->showError();
return true;
} else if (err == qstr("PASSWORD_EMPTY")) {
intro()->onBack();
goBack();
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
@@ -226,10 +169,10 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
return false;
}
bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
bool PwdCheckWidget::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
showError(lang(lng_flood_error));
_codeField->notaBene();
_codeField->showError();
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
@@ -240,7 +183,7 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
_codeField->setDisabled(false);
const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) {
intro()->onBack();
goBack();
return true;
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
recoverStartFail(error);
@@ -252,7 +195,7 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
} else if (err == qstr("CODE_INVALID")) {
showError(lang(lng_signin_wrong_code));
_codeField->selectAll();
_codeField->notaBene();
_codeField->showError();
return true;
}
if (cDebug()) { // internal server error
@@ -264,39 +207,42 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
return false;
}
void IntroPwdCheck::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = st::introFont->elided(lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern)), _textRect.width());
update();
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = qs(result.c_auth_passwordRecovery().vemail_pattern);
updateDescriptionText();
}
bool IntroPwdCheck::recoverStartFail(const RPCError &error) {
bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
stopCheck();
_pwdField->setDisabled(false);
_codeField->setDisabled(false);
_pwdField->show();
_pwdHint->show();
_codeField->hide();
_pwdField->setFocus();
updateDescriptionText();
update();
showError(QString());
hideError();
return true;
}
void IntroPwdCheck::onToRecover() {
void PwdCheckWidget::onToRecover() {
if (_hasRecovery) {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
showError(QString());
hideError();
_toRecover->hide();
_toPassword->show();
_pwdField->hide();
_pwdHint->hide();
_pwdField->setText(QString());
_codeField->show();
_codeField->setFocus();
updateDescriptionText();
if (_emailPattern.isEmpty()) {
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&IntroPwdCheck::recoverStarted), rpcFail(&IntroPwdCheck::recoverStartFail));
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PwdCheckWidget::recoverStarted), rpcFail(&PwdCheckWidget::recoverStartFail));
}
update();
} else {
ConfirmBox *box = new InformBox(lang(lng_signin_no_email_forgot));
Ui::showLayer(box);
@@ -304,101 +250,63 @@ void IntroPwdCheck::onToRecover() {
}
}
void IntroPwdCheck::onToPassword() {
void PwdCheckWidget::onToPassword() {
ConfirmBox *box = new InformBox(lang(lng_signin_cant_email_forgot));
Ui::showLayer(box);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset()));
}
void IntroPwdCheck::onToReset() {
void PwdCheckWidget::onToReset() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
_toRecover->show();
_toPassword->hide();
_pwdField->show();
_pwdHint->show();
_codeField->hide();
_codeField->setText(QString());
_pwdField->setFocus();
_reset->show();
showResetButton();
updateDescriptionText();
update();
}
void IntroPwdCheck::onReset() {
if (_sentRequest) return;
ConfirmBox *box = new ConfirmBox(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton);
connect(box, SIGNAL(confirmed()), this, SLOT(onResetSure()));
Ui::showLayer(box);
void PwdCheckWidget::updateDescriptionText() {
setDescriptionText(_pwdField->isHidden() ? lng_signin_recover_desc(lt_email, _emailPattern) : lang(lng_signin_desc));
}
void IntroPwdCheck::onResetSure() {
if (_sentRequest) return;
_sentRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&IntroPwdCheck::deleteDone), rpcFail(&IntroPwdCheck::deleteFail));
void PwdCheckWidget::onInputChange() {
hideError();
}
bool IntroPwdCheck::deleteFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_sentRequest = 0;
auto type = error.type();
if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) {
int seconds = type.mid(qstr("2FA_CONFIRM_WAIT_").size()).toInt();
int days = (seconds + 59) / 86400;
int hours = ((seconds + 59) % 86400) / 3600;
int minutes = ((seconds + 59) % 3600) / 60;
QString when;
if (days > 0) {
when = lng_signin_reset_in_days(lt_count_days, days, lt_count_hours, hours, lt_count_minutes, minutes);
} else if (hours > 0) {
when = lng_signin_reset_in_hours(lt_count_hours, hours, lt_count_minutes, minutes);
} else {
when = lng_signin_reset_in_minutes(lt_count_minutes, minutes);
}
Ui::showLayer(new InformBox(lng_signin_reset_wait(lt_phone_number, App::formatPhone(intro()->getPhone()), lt_when, when)));
} else if (type == qstr("2FA_RECENT_CONFIRM")) {
Ui::showLayer(new InformBox(lang(lng_signin_reset_cancelled)));
} else {
Ui::hideLayer();
showError(lang(lng_server_error));
}
return true;
}
void IntroPwdCheck::deleteDone(const MTPBool &v) {
Ui::hideLayer();
intro()->replaceStep(new IntroSignup(intro()));
}
void IntroPwdCheck::onInputChange() {
showError(QString());
}
void IntroPwdCheck::onSubmitPwd(bool force) {
void PwdCheckWidget::submit() {
if (_sentRequest) return;
if (_pwdField->isHidden()) {
if (!force && !_codeField->isEnabled()) return;
QString code = _codeField->text().trimmed();
if (!_codeField->isEnabled()) return;
auto code = _codeField->getLastText().trimmed();
if (code.isEmpty()) {
_codeField->notaBene();
_codeField->showError();
return;
}
_sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&IntroPwdCheck::pwdSubmitDone, true), rpcFail(&IntroPwdCheck::codeSubmitFail));
_sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&PwdCheckWidget::pwdSubmitDone, true), rpcFail(&PwdCheckWidget::codeSubmitFail));
} else {
if (!force && !_pwdField->isEnabled()) return;
if (!_pwdField->isEnabled()) return;
_pwdField->setDisabled(true);
setFocus();
showError(QString());
hideError();
QByteArray pwdData = _salt + _pwdField->text().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized);
QByteArray pwdData = _salt + _pwdField->getLastText().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized);
hashSha256(pwdData.constData(), pwdData.size(), pwdHash.data());
_sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&IntroPwdCheck::pwdSubmitDone, false), rpcFail(&IntroPwdCheck::pwdSubmitFail));
_sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&PwdCheckWidget::pwdSubmitDone, false), rpcFail(&PwdCheckWidget::pwdSubmitFail));
}
}
void IntroPwdCheck::onSubmit() {
onSubmitPwd();
QString PwdCheckWidget::nextButtonText() const {
return lang(lng_intro_submit);
}
} // namespace Intro

View File

@@ -23,26 +23,37 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "intro/introwidget.h"
namespace Ui {
class FlatInput;
class InputField;
class PasswordInput;
class RoundButton;
class LinkButton;
} // namespace Ui
class IntroPwdCheck final : public IntroStep {
namespace Intro {
class PwdCheckWidget : public Widget::Step {
Q_OBJECT
public:
IntroPwdCheck(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void step_error(float64 ms, bool timer);
PwdCheckWidget(QWidget *parent, Widget::Data *data);
void setInnerFocus() override;
void activate() override;
void cancelled() override;
void onSubmit() override;
void submit() override;
QString nextButtonText() const override;
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onToRecover();
void onToPassword();
void onInputChange();
void onCheckRequest();
void onToReset();
private:
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
bool pwdSubmitFail(const RPCError &error);
bool codeSubmitFail(const RPCError &error);
@@ -50,46 +61,24 @@ public:
void recoverStarted(const MTPauth_PasswordRecovery &result);
public slots:
void onSubmitPwd(bool force = false);
void onToRecover();
void onToPassword();
void onInputChange();
void onCheckRequest();
void onToReset();
void onReset();
void onResetSure();
private:
void showError(const QString &error);
void updateDescriptionText();
void stopCheck();
void deleteDone(const MTPBool &result);
bool deleteFail(const RPCError &error);
QString _error;
anim::fvalue a_errorAlpha;
Animation _a_error;
ChildWidget<Ui::RoundButton> _next;
QRect _textRect;
QByteArray _salt;
bool _hasRecovery;
QString _hint, _emailPattern;
ChildWidget<Ui::FlatInput> _pwdField;
ChildWidget<Ui::FlatInput> _codeField;
ChildWidget<Ui::PasswordInput> _pwdField;
ChildWidget<Ui::FlatLabel> _pwdHint;
ChildWidget<Ui::InputField> _codeField;
ChildWidget<Ui::LinkButton> _toRecover;
ChildWidget<Ui::LinkButton> _toPassword;
ChildWidget<Ui::LinkButton> _reset;
mtpRequestId _sentRequest = 0;
Text _hintText;
QByteArray _pwdSalt;
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@@ -29,200 +29,106 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/buttons/peer_avatar_button.h"
IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, a_photoOver(0)
, _a_error(animation(this, &IntroSignup::step_error))
, _a_photo(animation(this, &IntroSignup::step_photo))
, _next(this, lang(lng_intro_finish), st::introNextButton)
namespace Intro {
SignupWidget::SignupWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _photo(this, st::introPhotoSize, st::introPhotoIconPosition)
, _first(this, st::introName, lang(lng_signup_firstname))
, _last(this, st::introName, lang(lng_signup_lastname))
, _invertOrder(langFirstNameGoesSecond())
, _checkRequest(this) {
setVisible(false);
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitName()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
_photo->setClickedCallback([this] {
auto imgExtensions = cImgExtensions();
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter();
_readPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
});
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
notifyFileQueryUpdated(update);
});
if (_invertOrder) {
setTabOrder(_last, _first);
}
setErrorCentered(true);
setTitleText(lang(lng_signup_title));
setDescriptionText(lang(lng_signup_desc));
setMouseTracking(true);
}
void IntroSignup::mouseMoveEvent(QMouseEvent *e) {
bool photoOver = QRect(_phLeft, _phTop, st::introPhotoSize, st::introPhotoSize).contains(e->pos());
if (photoOver != _photoOver) {
_photoOver = photoOver;
if (_photoSmall.isNull()) {
a_photoOver.start(_photoOver ? 1 : 0);
_a_photo.start();
}
void SignupWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) {
if (_readPhotoFileQueryId != update.queryId) {
return;
}
_readPhotoFileQueryId = 0;
if (update.remoteContent.isEmpty() && update.filePaths.isEmpty()) {
return;
}
setCursor(_photoOver ? style::cur_pointer : style::cur_default);
}
void IntroSignup::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (QRect(_phLeft, _phTop, st::introPhotoSize, st::introPhotoSize).contains(e->pos())) {
QStringList imgExtensions(cImgExtensions());
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter());
QImage img;
QString file;
QByteArray remoteContent;
if (filedialogGetOpenFile(file, remoteContent, lang(lng_choose_images), filter)) {
if (!remoteContent.isEmpty()) {
img = App::readImage(remoteContent);
} else {
if (!file.isEmpty()) {
img = App::readImage(file);
}
}
} else {
return;
}
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
showError(lang(lng_bad_photo));
return;
}
PhotoCropBox *box = new PhotoCropBox(img, PeerId(0));
connect(box, SIGNAL(ready(const QImage &)), this, SLOT(onPhotoReady(const QImage &)));
Ui::showLayer(box);
}
}
void IntroSignup::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
Painter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, lang(lng_signup_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(_textRect, lang(lng_signup_desc), style::al_bottom);
}
if (_a_error.animating() || error.length()) {
p.setOpacity(a_errorAlpha.current());
QRect errRect;
if (_invertOrder) {
errRect = QRect((width() - st::introErrorWidth) / 2, (_first->y() + _first->height() + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight);
} else {
errRect = QRect((width() - st::introErrorWidth) / 2, (_last->y() + _last->height() + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight);
}
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(errRect, error, QTextOption(style::al_center));
p.setOpacity(1);
}
if (_photoSmall.isNull()) {
float64 o = a_photoOver.current();
QRect phRect(_phLeft, _phTop, st::introPhotoSize, st::introPhotoSize);
if (o > 0) {
if (o < 1) {
QColor c;
c.setRedF(st::newGroupPhotoBg->c.redF() * (1. - o) + st::newGroupPhotoBgOver->c.redF() * o);
c.setGreenF(st::newGroupPhotoBg->c.greenF() * (1. - o) + st::newGroupPhotoBgOver->c.greenF() * o);
c.setBlueF(st::newGroupPhotoBg->c.blueF() * (1. - o) + st::newGroupPhotoBgOver->c.blueF() * o);
p.fillRect(phRect, c);
} else {
p.fillRect(phRect, st::newGroupPhotoBgOver);
}
} else {
p.fillRect(phRect, st::newGroupPhotoBg);
}
st::newGroupPhotoIcon.paintInCenter(p, phRect);
QImage img;
if (!update.remoteContent.isEmpty()) {
img = App::readImage(update.remoteContent);
} else {
p.drawPixmap(_phLeft, _phTop, _photoSmall);
img = App::readImage(update.filePaths.front());
}
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
showError(lang(lng_bad_photo));
return;
}
auto box = new PhotoCropBox(img, PeerId(0));
connect(box, SIGNAL(ready(const QImage &)), this, SLOT(onPhotoReady(const QImage &)));
Ui::showLayer(box);
}
void IntroSignup::resizeEvent(QResizeEvent *e) {
_phLeft = (width() - _next->width()) / 2;
_phTop = st::introTextTop + st::introTextSize.height() + st::introCountry.top;
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
if (_invertOrder) {
_last->move((width() - _next->width()) / 2 + _next->width() - _last->width(), _phTop);
_first->move((width() - _next->width()) / 2 + _next->width() - _first->width(), _last->y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop);
} else {
_first->move((width() - _next->width()) / 2 + _next->width() - _first->width(), _phTop);
_last->move((width() - _next->width()) / 2 + _next->width() - _last->width(), _first->y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop);
}
}
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
}
void SignupWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
void IntroSignup::showError(const QString &err) {
if (!_a_error.animating() && err == error) return;
auto photoRight = contentLeft() + st::introNextButton.width;
auto photoTop = contentTop() + st::introPhotoTop;
_photo->moveToLeft(photoRight - _photo->width(), photoTop);
if (err.length()) {
error = err;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_a_error.start();
}
void IntroSignup::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
error.clear();
}
} else {
a_errorAlpha.update(dt, anim::linear);
}
if (timer) update();
}
void IntroSignup::step_photo(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_photo.stop();
a_photoOver.finish();
} else {
a_photoOver.update(dt, anim::linear);
}
if (timer) update();
}
void IntroSignup::activate() {
IntroStep::activate();
auto firstTop = contentTop() + st::introStepFieldTop;
auto secondTop = firstTop + st::introName.height + st::introPhoneTop;
if (_invertOrder) {
_last->moveToLeft(contentLeft(), firstTop);
_first->moveToLeft(contentLeft(), secondTop);
} else {
_first->moveToLeft(contentLeft(), firstTop);
_last->moveToLeft(contentLeft(), secondTop);
}
}
void SignupWidget::setInnerFocus() {
if (_invertOrder || _last->hasFocus()) {
_last->setFocus();
} else {
_first->setFocus();
}
}
void IntroSignup::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
void SignupWidget::activate() {
Step::activate();
_first->show();
_last->show();
_photo->show();
setInnerFocus();
}
void IntroSignup::stopCheck() {
void SignupWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
}
void SignupWidget::stopCheck() {
_checkRequest->stop();
}
void IntroSignup::onCheckRequest() {
void SignupWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@@ -244,13 +150,12 @@ void IntroSignup::onCheckRequest() {
}
}
void IntroSignup::onPhotoReady(const QImage &img) {
_photoBig = img;
_photoSmall = App::pixmapFromImageInPlace(img.scaled(st::introPhotoSize * cIntRetinaFactor(), st::introPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
_photoSmall.setDevicePixelRatio(cRetinaFactor());
void SignupWidget::onPhotoReady(const QImage &img) {
_photoImage = img;
_photo->setImage(_photoImage);
}
void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) {
void SignupWidget::nameSubmitDone(const MTPauth_Authorization &result) {
stopCheck();
_first->setDisabled(false);
_last->setDisabled(false);
@@ -259,10 +164,10 @@ void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) {
showError(lang(lng_server_error));
return;
}
intro()->finish(d.vuser, _photoBig);
finish(d.vuser, _photoImage);
}
bool IntroSignup::nameSubmitFail(const RPCError &error) {
bool SignupWidget::nameSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
stopCheck();
_first->setDisabled(false);
@@ -284,7 +189,7 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) {
if (err == qstr("PHONE_NUMBER_INVALID") || err == qstr("PHONE_CODE_EXPIRED") ||
err == qstr("PHONE_CODE_EMPTY") || err == qstr("PHONE_CODE_INVALID") ||
err == qstr("PHONE_NUMBER_OCCUPIED")) {
intro()->onBack();
goBack();
return true;
} else if (err == "FIRSTNAME_INVALID") {
showError(lang(lng_bad_name));
@@ -308,29 +213,29 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) {
return false;
}
void IntroSignup::onInputChange() {
void SignupWidget::onInputChange() {
showError(QString());
}
void IntroSignup::onSubmitName(bool force) {
void SignupWidget::submit() {
if (_invertOrder) {
if ((_last->hasFocus() || _last->text().trimmed().length()) && !_first->text().trimmed().length()) {
if ((_last->hasFocus() || _last->getLastText().trimmed().length()) && !_first->getLastText().trimmed().length()) {
_first->setFocus();
return;
} else if (!_last->text().trimmed().length()) {
} else if (!_last->getLastText().trimmed().length()) {
_last->setFocus();
return;
}
} else {
if ((_first->hasFocus() || _first->text().trimmed().length()) && !_last->text().trimmed().length()) {
if ((_first->hasFocus() || _first->getLastText().trimmed().length()) && !_last->getLastText().trimmed().length()) {
_last->setFocus();
return;
} else if (!_first->text().trimmed().length()) {
} else if (!_first->getLastText().trimmed().length()) {
_first->setFocus();
return;
}
}
if (!force && !_first->isEnabled()) return;
if (!_first->isEnabled()) return;
_first->setDisabled(true);
_last->setDisabled(true);
@@ -338,11 +243,13 @@ void IntroSignup::onSubmitName(bool force) {
showError(QString());
_firstName = _first->text().trimmed();
_lastName = _last->text().trimmed();
_sentRequest = MTP::send(MTPauth_SignUp(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(intro()->getCode()), MTP_string(_firstName), MTP_string(_lastName)), rpcDone(&IntroSignup::nameSubmitDone), rpcFail(&IntroSignup::nameSubmitFail));
_firstName = _first->getLastText().trimmed();
_lastName = _last->getLastText().trimmed();
_sentRequest = MTP::send(MTPauth_SignUp(MTP_string(getData()->phone), MTP_string(getData()->phoneHash), MTP_string(getData()->code), MTP_string(_firstName), MTP_string(_lastName)), rpcDone(&SignupWidget::nameSubmitDone), rpcFail(&SignupWidget::nameSubmitFail));
}
void IntroSignup::onSubmit() {
onSubmitName();
QString SignupWidget::nextButtonText() const {
return lang(lng_intro_finish);
}
} // namespace Intro

View File

@@ -21,64 +21,58 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "intro/introwidget.h"
#include "ui/filedialog.h"
namespace Ui {
class RoundButton;
class FlatInput;
class InputField;
class NewAvatarButton;
} // namespace Ui
class IntroSignup final : public IntroStep {
namespace Intro {
class SignupWidget : public Widget::Step, private base::Subscriber {
Q_OBJECT
public:
IntroSignup(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void step_error(float64 ms, bool timer);
void step_photo(float64 ms, bool timer);
SignupWidget(QWidget *parent, Widget::Data *data);
void setInnerFocus() override;
void activate() override;
void cancelled() override;
void onSubmit() override;
void submit() override;
QString nextButtonText() const override;
void nameSubmitDone(const MTPauth_Authorization &result);
bool nameSubmitFail(const RPCError &error);
protected:
void resizeEvent(QResizeEvent *e) override;
public slots:
void onSubmitName(bool force = false);
private slots:
void onInputChange();
void onCheckRequest();
void onPhotoReady(const QImage &img);
private:
void showError(const QString &err);
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
void nameSubmitDone(const MTPauth_Authorization &result);
bool nameSubmitFail(const RPCError &error);
void stopCheck();
QString error;
anim::fvalue a_errorAlpha, a_photoOver;
Animation _a_error;
Animation _a_photo;
QImage _photoImage;
ChildWidget<Ui::RoundButton> _next;
QRect _textRect;
bool _photoOver = false;
QImage _photoBig;
QPixmap _photoSmall;
int32 _phLeft, _phTop;
ChildWidget<Ui::FlatInput> _first;
ChildWidget<Ui::FlatInput> _last;
ChildWidget<Ui::NewAvatarButton> _photo;
ChildWidget<Ui::InputField> _first;
ChildWidget<Ui::InputField> _last;
QString _firstName, _lastName;
mtpRequestId _sentRequest = 0;
FileDialog::QueryId _readPhotoFileQueryId = 0;
bool _invertOrder = false;
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@@ -24,67 +24,24 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "application.h"
#include "intro/introphone.h"
#include "langloaderplain.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent)
, _intro(this, lang(lng_intro), Ui::FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle)
, _changeLang(this, QString())
, _next(this, lang(lng_start_msgs), st::introNextButton) {
_changeLang->hide();
if (cLang() == languageDefault) {
int32 l = Sandbox::LangSystem();
if (l != languageDefault) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this));
QString text = loader.found().value(lng_switch_to_this);
if (!text.isEmpty()) {
_changeLang->setText(text);
parent->langChangeTo(l);
_changeLang->show();
}
}
} else {
_changeLang->setText(langOriginal(lng_switch_to_this));
parent->langChangeTo(languageDefault);
_changeLang->show();
}
_headerWidth = st::introHeaderFont->width(qsl("Telegram Desktop"));
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), parent, SLOT(onStepSubmit()));
connect(_changeLang, SIGNAL(clicked()), parent, SLOT(onChangeLang()));
namespace Intro {
StartWidget::StartWidget(QWidget *parent, Widget::Data *data) : Step(parent, data, true) {
setMouseTracking(true);
setTitleText(qsl("Telegram Desktop"));
setDescriptionText(lang(lng_intro_about));
show();
}
void IntroStart::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
Painter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
int32 hy = _intro->y() - st::introHeaderFont->height - st::introHeaderSkip + st::introHeaderFont->ascent;
p.setFont(st::introHeaderFont);
p.setPen(st::introHeaderFg);
p.drawText((width() - _headerWidth) / 2, hy, qsl("Telegram Desktop"));
st::introIcon.paint(p, QPoint((width() - st::introIcon.width()) / 2, hy - st::introIconSkip - st::introIcon.height()), width());
void StartWidget::submit() {
goNext(new Intro::PhoneWidget(parentWidget(), getData()));
}
void IntroStart::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_intro->move((width() - _intro->width()) / 2, _next->y() - _intro->height() - st::introSkip);
_changeLang->move((width() - _changeLang->width()) / 2, _next->y() + _next->height() + _changeLang->height());
}
QString StartWidget::nextButtonText() const {
return lang(lng_start_msgs);
}
void IntroStart::onSubmit() {
intro()->nextStep(new IntroPhone(intro()));
}
} // namespace Intro

View File

@@ -28,22 +28,15 @@ class LinkButton;
class RoundButton;
} // namespace Ui
class IntroStart final : public IntroStep {
namespace Intro {
class StartWidget : public Widget::Step {
public:
IntroStart(IntroWidget *parent);
StartWidget(QWidget *parent, Widget::Data *data);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void onSubmit() override;
private:
ChildWidget<Ui::FlatLabel> _intro;
ChildWidget<Ui::LinkButton> _changeLang;
ChildWidget<Ui::RoundButton> _next;
int32 _headerWidth = 0;
void submit() override;
QString nextButtonText() const override;
};
} // namespace Intro

View File

@@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "localstorage.h"
#include "langloaderplain.h"
#include "intro/introstart.h"
#include "intro/introphone.h"
#include "intro/introcode.h"
@@ -31,39 +32,60 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "mainwindow.h"
#include "application.h"
#include "boxes/confirmbox.h"
#include "ui/text/text.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/effects/widget_fade_wrap.h"
#include "styles/style_intro.h"
#include "ui/effects/slide_animation.h"
#include "autoupdater.h"
#include "window/slide_animation.h"
#include "window/window_slide_animation.h"
#include "styles/style_boxes.h"
#include "styles/style_intro.h"
#include "styles/style_window.h"
IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent)
, _a_stage(animation(this, &IntroWidget::step_stage))
, _a_show(animation(this, &IntroWidget::step_show))
namespace Intro {
Widget::Widget(QWidget *parent) : TWidget(parent)
, _a_show(animation(this, &Widget::step_show))
, _back(this, new Ui::IconButton(this, st::introBackButton), base::lambda<void()>(), st::introSlideDuration)
, _settings(this, lang(lng_menu_settings), st::defaultBoxButton) {
_back->entity()->setClickedCallback([this] { onBack(); });
, _settings(this, new Ui::RoundButton(this, lang(lng_menu_settings), st::defaultBoxButton), base::lambda<void()>(), st::introCoverDuration)
, _next(this, QString(), st::introNextButton) {
getData()->country = psCurrentCountry();
_back->entity()->setClickedCallback([this] { historyMove(Direction::Back); });
_back->hideFast();
_settings->setClickedCallback([] { App::wnd()->showSettings(); });
_next->setClickedCallback([this] { getStep()->submit(); });
_countryForReg = psCurrentCountry();
_settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); });
MTP::send(MTPhelp_GetNearestDc(), rpcDone(&IntroWidget::gotNearestDC));
if (cLang() == languageDefault) {
auto systemLangId = Sandbox::LangSystem();
if (systemLangId != languageDefault) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this));
QString text = loader.found().value(lng_switch_to_this);
if (!text.isEmpty()) {
_changeLanguage.create(this, new Ui::LinkButton(this, text), base::lambda<void()>(), st::introCoverDuration);
_changeLanguage->entity()->setClickedCallback([this, systemLangId] { changeLanguage(systemLangId); });
}
}
} else {
_changeLanguage.create(this, new Ui::LinkButton(this, langOriginal(lng_switch_to_this)), base::lambda<void()>(), st::introCoverDuration);
_changeLanguage->entity()->setClickedCallback([this] { changeLanguage(languageDefault); });
}
_stepHistory.push_back(new IntroStart(this));
_back->raise();
_settings->raise();
MTP::send(MTPhelp_GetNearestDc(), rpcDone(&Widget::gotNearestDC));
appendStep(new StartWidget(this, getData()));
fixOrder();
show();
setFocus();
showControls();
getStep()->showFast();
cSetPasswordRecovered(false);
_back->moveToLeft(0, 0);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus()));
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus()));
@@ -74,12 +96,12 @@ IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent)
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void IntroWidget::onCheckUpdateStatus() {
void Widget::onCheckUpdateStatus() {
if (Sandbox::updatingState() == Application::UpdatingReady) {
if (_update) return;
_update.create(this, lang(lng_menu_update).toUpper(), st::defaultBoxButton);
_update.create(this, new Ui::RoundButton(this, lang(lng_menu_update).toUpper(), st::defaultBoxButton), base::lambda<void()>(), st::introCoverDuration);
if (!_a_show.animating()) _update->show();
_update->setClickedCallback([] {
_update->entity()->setClickedCallback([] {
checkReadyUpdate();
App::restart();
});
@@ -91,123 +113,204 @@ void IntroWidget::onCheckUpdateStatus() {
}
#endif // TDESKTOP_DISABLE_AUTOUPDATE
void IntroWidget::langChangeTo(int32 langId) {
_langChangeTo = langId;
}
void IntroWidget::onChangeLang() {
cSetLang(_langChangeTo);
void Widget::changeLanguage(int32 languageId) {
cSetLang(languageId);
Local::writeSettings();
App::restart();
}
void IntroWidget::onStepSubmit() {
step()->onSubmit();
void Widget::setInnerFocus() {
if (getStep()->animating()) {
setFocus();
} else {
getStep()->setInnerFocus();
}
}
void IntroWidget::onBack() {
historyMove(MoveBack);
}
void IntroWidget::historyMove(MoveType type) {
if (_a_stage.animating()) return;
void Widget::historyMove(Direction direction) {
if (getStep()->animating()) return;
t_assert(_stepHistory.size() > 1);
if (App::app()) App::app()->mtpPause();
switch (type) {
case MoveBack: {
_cacheHide = grabStep();
IntroStep *back = step();
_backFrom = back->hasBack() ? 1 : 0;
auto wasStep = getStep((direction == Direction::Back) ? 0 : 1);
if (direction == Direction::Back) {
_stepHistory.pop_back();
back->cancelled();
delete back;
} break;
case MoveForward: {
_cacheHide = grabStep(1);
_backFrom = step(1)->hasBack() ? 1 : 0;
step(1)->finished();
} break;
case MoveReplace: {
_cacheHide = grabStep(1);
IntroStep *replaced = step(1);
_backFrom = replaced->hasBack() ? 1 : 0;
wasStep->cancelled();
} else if (direction == Direction::Replace) {
_stepHistory.removeAt(_stepHistory.size() - 2);
replaced->finished();
delete replaced;
} break;
}
getStep()->prepareShowAnimated(wasStep);
if (wasStep->hasCover() != getStep()->hasCover()) {
_nextTopFrom = wasStep->contentTop() + st::introStepHeight;
_controlsTopFrom = wasStep->hasCover() ? st::introCoverHeight : 0;
_coverShownAnimation.start([this] { updateControlsGeometry(); }, 0., 1., st::introCoverDuration, anim::easeOutCirc);
}
_cacheShow = grabStep();
_backTo = step()->hasBack() ? 1 : 0;
int32 m = (type == MoveBack) ? -1 : 1;
a_coordHide = anim::ivalue(0, -m * st::introSlideShift);
a_opacityHide = anim::fvalue(1, 0);
a_coordShow = anim::ivalue(m * st::introSlideShift, 0);
a_opacityShow = anim::fvalue(0, 1);
_a_stage.start();
_a_stage.step();
if (_backTo) {
if (direction == Direction::Forward || direction == Direction::Replace) {
wasStep->finished();
}
if (direction == Direction::Back || direction == Direction::Replace) {
delete base::take(wasStep);
}
if (getStep()->hasBack()) {
_back->fadeIn();
} else {
_back->fadeOut();
}
step()->hide();
if (getStep()->hasCover()) {
_settings->fadeOut();
if (_update) _update->fadeOut();
if (_changeLanguage) _changeLanguage->fadeIn();
} else {
_settings->fadeIn();
if (_update) _update->fadeIn();
if (_changeLanguage) _changeLanguage->fadeOut();
}
_next->setText(getStep()->nextButtonText());
if (_resetAccount) _resetAccount->fadeOut();
getStep()->showAnimated(direction);
fixOrder();
}
void IntroWidget::pushStep(IntroStep *step, MoveType type) {
_stepHistory.push_back(step);
void Widget::fixOrder() {
_next->raise();
if (_update) _update->raise();
_settings->raise();
_back->raise();
}
void Widget::moveToStep(Step *step, Direction direction) {
appendStep(step);
_back->raise();
_settings->raise();
if (_update) {
_update->raise();
}
_stepHistory.back()->hide();
historyMove(type);
historyMove(direction);
}
void IntroWidget::gotNearestDC(const MTPNearestDc &result) {
const auto &nearest(result.c_nearestDc());
void Widget::appendStep(Step *step) {
_stepHistory.push_back(step);
step->setGeometry(calculateStepRect());
step->setGoCallback([this](Step *step, Direction direction) {
if (direction == Direction::Back) {
historyMove(direction);
} else {
moveToStep(step, direction);
}
});
step->setShowResetCallback([this] {
showResetButton();
});
}
void Widget::showResetButton() {
if (!_resetAccount) {
_resetAccount.create(this, new Ui::RoundButton(this, lang(lng_signin_reset_account), st::introResetButton), base::lambda<void()>(), st::introErrorDuration);
_resetAccount->hideFast();
_resetAccount->entity()->setClickedCallback([this] { resetAccount(); });
updateControlsGeometry();
}
_resetAccount->fadeIn();
}
void Widget::resetAccount() {
if (_resetRequest) return;
auto box = new ConfirmBox(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton);
box->setConfirmedCallback([this] { resetAccountSure(); });
Ui::showLayer(box);
}
void Widget::resetAccountSure() {
if (_resetRequest) return;
_resetRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&Widget::resetDone), rpcFail(&Widget::resetFail));
}
void Widget::resetDone(const MTPBool &result) {
Ui::hideLayer();
moveToStep(new SignupWidget(this, getData()), Direction::Replace);
}
bool Widget::resetFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_resetRequest = 0;
auto type = error.type();
if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) {
int seconds = type.mid(qstr("2FA_CONFIRM_WAIT_").size()).toInt();
int days = (seconds + 59) / 86400;
int hours = ((seconds + 59) % 86400) / 3600;
int minutes = ((seconds + 59) % 3600) / 60;
QString when;
if (days > 0) {
when = lng_signin_reset_in_days(lt_count_days, days, lt_count_hours, hours, lt_count_minutes, minutes);
} else if (hours > 0) {
when = lng_signin_reset_in_hours(lt_count_hours, hours, lt_count_minutes, minutes);
} else {
when = lng_signin_reset_in_minutes(lt_count_minutes, minutes);
}
Ui::showLayer(new InformBox(lng_signin_reset_wait(lt_phone_number, App::formatPhone(getData()->phone), lt_when, when)));
} else if (type == qstr("2FA_RECENT_CONFIRM")) {
Ui::showLayer(new InformBox(lang(lng_signin_reset_cancelled)));
} else {
Ui::hideLayer();
getStep()->showError(lang(lng_server_error));
}
return true;
}
void Widget::gotNearestDC(const MTPNearestDc &result) {
auto &nearest = result.c_nearestDc();
DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3").arg(nearest.vcountry.c_string().v.c_str()).arg(nearest.vnearest_dc.v).arg(nearest.vthis_dc.v));
MTP::setdc(result.c_nearestDc().vnearest_dc.v, true);
if (_countryForReg != nearest.vcountry.c_string().v.c_str()) {
_countryForReg = nearest.vcountry.c_string().v.c_str();
emit countryChanged();
MTP::setdc(nearest.vnearest_dc.v, true);
auto nearestCountry = qs(nearest.vcountry);
if (getData()->country != nearestCountry) {
getData()->country = nearestCountry;
getData()->updated.notify();
}
}
QPixmap IntroWidget::grabStep(int skip) {
return myGrab(step(skip), QRect(st::introSlideShift, 0, st::introSize.width(), st::introSize.height()));
void Widget::showControls() {
getStep()->show();
_next->show();
_next->setText(getStep()->nextButtonText());
if (getStep()->hasCover()) {
_settings->hideFast();
if (_update) _update->hideFast();
if (_changeLanguage) _changeLanguage->showFast();
} else {
_settings->showFast();
if (_update) _update->showFast();
if (_changeLanguage) _changeLanguage->hideFast();
}
if (getStep()->hasBack()) {
_back->showFast();
} else {
_back->hideFast();
}
}
void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) {
void Widget::hideControls() {
getStep()->hide();
_next->hide();
_settings->hideFast();
if (_update) _update->hideFast();
if (_changeLanguage) _changeLanguage->hideFast();
_back->hideFast();
}
void Widget::animShow(const QPixmap &bgAnimCache, bool back) {
if (App::app()) App::app()->mtpPause();
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
_a_show.stop();
step()->show();
_settings->show();
if (_update) _update->show();
if (step()->hasBack()) {
_back->showFast();
} else {
_back->hideFast();
}
showControls();
(back ? _cacheUnder : _cacheOver) = myGrab(this);
step()->hide();
_back->hideFast();
_settings->hide();
if (_update) _update->hide();
hideControls();
a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift);
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
@@ -217,7 +320,7 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) {
show();
}
void IntroWidget::step_show(float64 ms, bool timer) {
void Widget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
if (dt >= 1) {
_a_show.stop();
@@ -228,13 +331,9 @@ void IntroWidget::step_show(float64 ms, bool timer) {
_cacheUnder = _cacheOver = QPixmap();
setFocus();
step()->activate();
if (step()->hasBack()) {
_back->showFast();
}
_settings->show();
if (_update) _update->show();
showControls();
getStep()->activate();
if (App::app()) App::app()->mtpUnpause();
} else {
a_coordUnder.update(dt, Window::SlideAnimation::transition());
@@ -244,37 +343,14 @@ void IntroWidget::step_show(float64 ms, bool timer) {
if (timer) update();
}
void IntroWidget::stop_show() {
_a_show.stop();
}
void IntroWidget::step_stage(float64 ms, bool timer) {
float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration;
float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0;
if (dt >= 1) {
_a_stage.stop();
a_coordShow.finish();
a_opacityShow.finish();
_cacheHide = _cacheShow = QPixmap();
setFocus();
step()->activate();
if (App::app()) App::app()->mtpUnpause();
} else {
a_coordShow.update(dt2, anim::easeOutCirc);
a_opacityShow.update(dt2, anim::easeInCirc);
a_coordHide.update(dt1, anim::easeInCirc);
a_opacityHide.update(dt1, anim::easeOutCirc);
}
if (timer) update();
}
void IntroWidget::paintEvent(QPaintEvent *e) {
void Widget::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
setMouseTracking(true);
if (_coverShownAnimation.animating()) {
_coverShownAnimation.step(getms());
}
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
@@ -290,130 +366,406 @@ void IntroWidget::paintEvent(QPaintEvent *e) {
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
p.setOpacity(a_shadow.current());
st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
} else if (_a_stage.animating()) {
p.setOpacity(a_opacityHide.current());
p.drawPixmap(step()->x() + st::introSlideShift + a_coordHide.current(), step()->y(), _cacheHide);
p.setOpacity(a_opacityShow.current());
p.drawPixmap(step()->x() + st::introSlideShift + a_coordShow.current(), step()->y(), _cacheShow);
}
}
QRect IntroWidget::innerRect() const {
int innerWidth = st::introSize.width() + 2 * st::introSlideShift, innerHeight = st::introSize.height();
return QRect((width() - innerWidth) / 2, (height() - innerHeight) / 2, innerWidth, (height() + innerHeight) / 2);
QRect Widget::calculateStepRect() const {
auto stepInnerTop = (height() - st::introHeight) / 2;
accumulate_max(stepInnerTop, st::introStepTopMin);
auto nextTop = stepInnerTop + st::introStepHeight;
auto additionalHeight = st::introStepHeightAdd;
auto stepWidth = width();
auto stepHeight = nextTop + additionalHeight;
return QRect(0, 0, stepWidth, stepHeight);
}
QString IntroWidget::currentCountry() const {
return _countryForReg;
}
void IntroWidget::setPhone(const QString &phone, const QString &phone_hash, bool registered) {
_phone = phone;
_phone_hash = phone_hash;
_registered = registered;
}
void IntroWidget::setCode(const QString &code) {
_code = code;
}
void IntroWidget::setPwdSalt(const QByteArray &salt) {
_pwdSalt = salt;
}
void IntroWidget::setHasRecovery(bool has) {
_hasRecovery = has;
}
void IntroWidget::setPwdHint(const QString &hint) {
_pwdHint = hint;
}
void IntroWidget::setCodeByTelegram(bool byTelegram) {
_codeByTelegram = byTelegram;
}
void IntroWidget::setCallStatus(const CallStatus &status) {
_callStatus = status;
}
const QString &IntroWidget::getPhone() const {
return _phone;
}
const QString &IntroWidget::getPhoneHash() const {
return _phone_hash;
}
const QString &IntroWidget::getCode() const {
return _code;
}
const IntroWidget::CallStatus &IntroWidget::getCallStatus() const {
return _callStatus;
}
const QByteArray &IntroWidget::getPwdSalt() const {
return _pwdSalt;
}
bool IntroWidget::getHasRecovery() const {
return _hasRecovery;
}
const QString &IntroWidget::getPwdHint() const {
return _pwdHint;
}
bool IntroWidget::codeByTelegram() const {
return _codeByTelegram;
}
void IntroWidget::resizeEvent(QResizeEvent *e) {
auto r = innerRect();
for (auto step : _stepHistory) {
step->setGeometry(r);
void Widget::resizeEvent(QResizeEvent *e) {
auto stepRect = calculateStepRect();
for_const (auto step, _stepHistory) {
step->setGeometry(stepRect);
}
updateControlsGeometry();
}
void IntroWidget::updateControlsGeometry() {
_settings->moveToRight(st::boxButtonPadding.right(), st::boxButtonPadding.top());
void Widget::moveControls() {
}
void Widget::updateControlsGeometry() {
auto shown = _coverShownAnimation.current(1.);
auto controlsTopTo = getStep()->hasCover() ? st::introCoverHeight : 0;
auto controlsTop = anim::interpolate(_controlsTopFrom, controlsTopTo, shown);
_settings->moveToRight(st::introSettingsSkip, controlsTop + st::introSettingsSkip);
if (_update) {
_update->moveToRight(st::boxButtonPadding.right() + _settings->width() + st::boxButtonPadding.left(), _settings->y());
_update->moveToRight(st::introSettingsSkip + _settings->width() + st::introSettingsSkip, _settings->y());
}
_back->moveToLeft(0, controlsTop);
auto nextTopTo = getStep()->contentTop() + st::introStepHeight;
auto nextTop = anim::interpolate(_nextTopFrom, nextTopTo, shown);
_next->moveToLeft((width() - _next->width()) / 2, nextTop);
if (_changeLanguage) {
_changeLanguage->moveToLeft((width() - _changeLanguage->width()) / 2, _next->y() + _next->height() + _changeLanguage->height());
}
if (_resetAccount) {
_resetAccount->moveToLeft((width() - _resetAccount->width()) / 2, height() - st::introResetBottom - _resetAccount->height());
}
}
void IntroWidget::finish(const MTPUser &user, const QImage &photo) {
void Widget::keyPressEvent(QKeyEvent *e) {
if (_a_show.animating() || getStep()->animating()) return;
if (e->key() == Qt::Key_Escape) {
if (getStep()->hasBack()) {
historyMove(Direction::Back);
}
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) {
getStep()->submit();
}
}
Widget::~Widget() {
for (auto step : base::take(_stepHistory)) {
delete step;
}
if (App::wnd()) App::wnd()->noIntro(this);
}
QString Widget::Step::nextButtonText() const {
return lang(lng_intro_next);
}
void Widget::Step::finish(const MTPUser &user, QImage photo) {
App::wnd()->setupMain(&user);
// "this" is already deleted here by creating the main widget.
if (!photo.isNull()) {
App::app()->uploadProfilePhoto(photo, MTP::authedId());
}
}
void IntroWidget::keyPressEvent(QKeyEvent *e) {
if (_a_show.animating() || _a_stage.animating()) return;
void Widget::Step::paintEvent(QPaintEvent *e) {
Painter p(this);
paintAnimated(p, e->rect());
}
if (e->key() == Qt::Key_Escape) {
if (step()->hasBack()) {
onBack();
void Widget::Step::resizeEvent(QResizeEvent *e) {
updateLabelsPosition();
}
void Widget::Step::updateLabelsPosition() {
myEnsureResized(_description->entity());
if (hasCover()) {
_title->moveToLeft((width() - _title->width()) / 2, contentTop() + st::introCoverTitleTop);
_description->moveToLeft((width() - _description->width()) / 2, contentTop() + st::introCoverDescriptionTop);
} else {
_title->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introTitleTop);
_description->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introDescriptionTop);
}
if (_error) {
if (_errorCentered) {
_error->entity()->resizeToWidth(width());
}
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) {
onStepSubmit();
myEnsureResized(_error->entity());
auto errorLeft = _errorCentered ? 0 : (contentLeft() + st::buttonRadius);
auto errorTop = contentTop() + (_errorBelowLink ? st::introErrorBelowLinkTop : st::introErrorTop);
_error->moveToLeft(errorLeft, errorTop);
}
}
void IntroWidget::rpcClear() {
for (IntroStep *step : _stepHistory) {
step->rpcClear();
void Widget::Step::setTitleText(QString richText) {
_title->setRichText(richText);
updateLabelsPosition();
}
void Widget::Step::setDescriptionText(QString richText) {
_description->entity()->setRichText(richText);
updateLabelsPosition();
}
void Widget::Step::showFinished() {
_a_show.finish();
_coverAnimation = CoverAnimation();
_slideAnimation.reset();
prepareCoverMask();
activate();
if (App::app()) App::app()->mtpUnpause();
}
bool Widget::Step::paintAnimated(Painter &p, QRect clip) {
if (_slideAnimation) {
_slideAnimation->paintFrame(p, (width() - st::introStepWidth) / 2, contentTop(), width(), getms());
if (!_slideAnimation->animating()) {
showFinished();
return false;
}
return true;
}
auto guard = base::scope_guard([this, &p] {
if (hasCover()) paintCover(p, 0);
});
auto dt = _a_show.current(getms(), 1.);
if (!_a_show.animating()) {
if (_coverAnimation.title) {
showFinished();
}
if (!QRect(0, contentTop(), width(), st::introStepHeight).intersects(clip)) {
return true;
}
return false;
}
auto easeOut = anim::easeOutCirc(1., dt);
auto arrivingAlpha = easeOut;
auto departingAlpha = 1. - easeOut;
auto showCoverMethod = easeOut;
auto hideCoverMethod = easeOut;
auto coverTop = (hasCover() ? anim::interpolate(-st::introCoverHeight, 0, showCoverMethod) : anim::interpolate(0, -st::introCoverHeight, hideCoverMethod));
paintCover(p, coverTop);
guard.dismiss();
auto positionReady = hasCover() ? showCoverMethod : hideCoverMethod;
_coverAnimation.title->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
_coverAnimation.description->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
paintContentSnapshot(p, _coverAnimation.contentSnapshotWas, departingAlpha, showCoverMethod);
paintContentSnapshot(p, _coverAnimation.contentSnapshotNow, arrivingAlpha, 1. - hideCoverMethod);
return true;
}
void Widget::Step::fillSentCodeData(const MTPauth_SentCodeType &type) {
switch (type.type()) {
case mtpc_auth_sentCodeTypeApp: {
getData()->codeByTelegram = true;
getData()->codeLength = type.c_auth_sentCodeTypeApp().vlength.v;
} break;
case mtpc_auth_sentCodeTypeSms: {
getData()->codeByTelegram = false;
getData()->codeLength = type.c_auth_sentCodeTypeSms().vlength.v;
} break;
case mtpc_auth_sentCodeTypeCall: {
getData()->codeByTelegram = false;
getData()->codeLength = type.c_auth_sentCodeTypeCall().vlength.v;
} break;
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
}
IntroWidget::~IntroWidget() {
while (!_stepHistory.isEmpty()) {
IntroStep *back = _stepHistory.back();
_stepHistory.pop_back();
delete back;
}
if (App::wnd()) App::wnd()->noIntro(this);
void Widget::Step::showDescription() {
_description->fadeIn();
}
void Widget::Step::hideDescription() {
_description->fadeOut();
}
void Widget::Step::paintContentSnapshot(Painter &p, const QPixmap &snapshot, float64 alpha, float64 howMuchHidden) {
if (!snapshot.isNull()) {
auto contentTop = anim::interpolate(height() - (snapshot.height() / cIntRetinaFactor()), height(), howMuchHidden);
if (contentTop < height()) {
p.setOpacity(alpha);
p.drawPixmap(QPoint(contentLeft(), contentTop), snapshot, QRect(0, 0, snapshot.width(), (height() - contentTop) * cIntRetinaFactor()));
}
}
}
void Widget::Step::prepareCoverMask() {
if (!_coverMask.isNull()) return;
auto maskWidth = cIntRetinaFactor();
auto maskHeight = st::introCoverHeight * cIntRetinaFactor();
auto mask = QImage(maskWidth, maskHeight, QImage::Format_ARGB32_Premultiplied);
auto maskInts = reinterpret_cast<uint32*>(mask.bits());
t_assert(mask.depth() == (sizeof(uint32) << 3));
auto maskIntsPerLineAdded = (mask.bytesPerLine() >> 2) - maskWidth;
t_assert(maskIntsPerLineAdded >= 0);
auto realHeight = static_cast<float64>(maskHeight - 1);
for (auto y = 0; y != maskHeight; ++y) {
auto color = anim::color(st::introCoverTopBg, st::introCoverBottomBg, y / realHeight);
auto colorInt = anim::getPremultiplied(color);
for (auto x = 0; x != maskWidth; ++x) {
*maskInts++ = colorInt;
}
maskInts += maskIntsPerLineAdded;
}
_coverMask = App::pixmapFromImageInPlace(std_::move(mask));
}
void Widget::Step::paintCover(Painter &p, int top) {
auto coverHeight = top + st::introCoverHeight;
if (coverHeight > 0) {
p.drawPixmap(QRect(0, 0, width(), coverHeight), _coverMask, QRect(0, -top * cIntRetinaFactor(), _coverMask.width(), coverHeight * cIntRetinaFactor()));
}
auto left = 0;
auto right = 0;
if (width() < st::introCoverMaxWidth) {
auto iconsMaxSkip = st::introCoverMaxWidth - st::introCoverLeft.width() - st::introCoverRight.width();
auto iconsSkip = st::introCoverIconsMinSkip + (iconsMaxSkip - st::introCoverIconsMinSkip) * (width() - st::introStepWidth) / (st::introCoverMaxWidth - st::introStepWidth);
auto outside = iconsSkip + st::introCoverLeft.width() + st::introCoverRight.width() - width();
left = -outside / 2;
right = -outside - left;
}
if (top < 0) {
auto shown = float64(coverHeight) / st::introCoverHeight;
auto leftShown = qRound(shown * (left + st::introCoverLeft.width()));
left = leftShown - st::introCoverLeft.width();
auto rightShown = qRound(shown * (right + st::introCoverRight.width()));
right = rightShown - st::introCoverRight.width();
}
st::introCoverLeft.paint(p, left, coverHeight - st::introCoverLeft.height(), width());
st::introCoverRight.paint(p, width() - right - st::introCoverRight.width(), coverHeight - st::introCoverRight.height(), width());
auto planeLeft = (width() - st::introCoverIcon.width()) / 2 - st::introCoverIconLeft;
auto planeTop = top + st::introCoverIconTop;
if (top < 0 && !_hasCover) {
auto deltaLeft = -qRound(float64(st::introPlaneWidth / st::introPlaneHeight) * top);
// auto deltaTop = top;
planeLeft += deltaLeft;
// planeTop += top;
}
st::introCoverIcon.paint(p, planeLeft, planeTop, width());
}
int Widget::Step::contentLeft() const {
return (width() - st::introNextButton.width) / 2;
}
int Widget::Step::contentTop() const {
auto result = height() - st::introStepHeight - st::introStepHeightAdd;
if (_hasCover) {
auto added = 1. - snap(float64(height() - st::windowMinHeight) / (st::introStepHeightFull - st::windowMinHeight), 0., 1.);
result += qRound(added * st::introStepHeightAdd);
}
return result;
}
void Widget::Step::setErrorCentered(bool centered) {
_errorCentered = centered;
_error.destroy();
}
void Widget::Step::setErrorBelowLink(bool below) {
_errorBelowLink = below;
if (_error) {
updateLabelsPosition();
}
}
void Widget::Step::showError(const QString &text) {
_errorText = text;
if (_errorText.isEmpty()) {
if (_error) _error->fadeOut();
} else {
if (!_error) {
auto &st = _errorCentered ? st::introErrorCentered : st::introError;
_error.create(this, new Ui::FlatLabel(this, st, st::introErrorTextStyle), base::lambda<void()>(), st::introErrorDuration);
_error->hideFast();
}
_error->entity()->setText(text);
updateLabelsPosition();
_error->fadeIn();
}
}
Widget::Step::Step(QWidget *parent, Data *data, bool hasCover) : TWidget(parent)
, _data(data)
, _hasCover(hasCover)
, _title(this, _hasCover ? st::introCoverTitle : st::introTitle, st::defaultTextStyle)
, _description(this, new Ui::FlatLabel(this, _hasCover ? st::introCoverDescription : st::introDescription, _hasCover ? st::introCoverDescriptionTextStyle : st::introDescriptionTextStyle), base::lambda<void()>(), st::introErrorDuration) {
hide();
}
void Widget::Step::prepareShowAnimated(Step *after) {
if (hasCover() || after->hasCover()) {
_coverAnimation = prepareCoverAnimation(after);
prepareCoverMask();
} else {
auto leftSnapshot = after->prepareSlideAnimation();
auto rightSnapshot = prepareSlideAnimation();
_slideAnimation = std_::make_unique<Ui::SlideAnimation>();
_slideAnimation->setSnapshots(std_::move(leftSnapshot), std_::move(rightSnapshot));
_slideAnimation->setOverflowHidden(false);
}
}
Widget::Step::CoverAnimation Widget::Step::prepareCoverAnimation(Step *after) {
auto result = CoverAnimation();
result.title = Ui::FlatLabel::CrossFade(after->_title, _title, st::introBg);
result.description = Ui::FlatLabel::CrossFade(after->_description->entity(), _description->entity(), st::introBg, after->_description->pos(), _description->pos());
result.contentSnapshotWas = after->prepareContentSnapshot();
result.contentSnapshotNow = prepareContentSnapshot();
return std_::move(result);
}
QPixmap Widget::Step::prepareContentSnapshot() {
auto otherTop = _description->y() + _description->height();
auto otherRect = myrtlrect(contentLeft(), otherTop, st::introStepWidth, height() - otherTop);
return myGrab(this, otherRect);
}
QPixmap Widget::Step::prepareSlideAnimation() {
auto grabLeft = (width() - st::introStepWidth) / 2;
auto grabTop = contentTop();
return myGrab(this, QRect(grabLeft, grabTop, st::introStepWidth, st::introStepHeight));
}
void Widget::Step::showAnimated(Direction direction) {
show();
if (App::app()) App::app()->mtpPause();
hideChildren();
if (_slideAnimation) {
auto slideLeft = (direction == Direction::Back);
_slideAnimation->start(slideLeft, [this] { update(0, contentTop(), width(), st::introStepHeight); }, st::introSlideDuration);
} else {
_a_show.start([this] { update(); }, 0., 1., st::introCoverDuration);
}
}
void Widget::Step::setGoCallback(base::lambda<void(Step *step, Direction direction)> &&callback) {
_goCallback = std_::move(callback);
}
void Widget::Step::setShowResetCallback(base::lambda<void()> &&callback) {
_showResetCallback = std_::move(callback);
}
void Widget::Step::showFast() {
show();
showFinished();
}
bool Widget::Step::animating() const {
return (_slideAnimation && _slideAnimation->animating()) || _a_show.animating();
}
bool Widget::Step::hasCover() const {
return _hasCover;
}
bool Widget::Step::hasBack() const {
return false;
}
void Widget::Step::activate() {
_title->show();
_description->show();
if (!_errorText.isEmpty()) {
_error->showFast();
}
}
void Widget::Step::cancelled() {
}
void Widget::Step::finished() {
hide();
}
} // namespace Intro

View File

@@ -23,77 +23,32 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class IconButton;
class RoundButton;
class LinkButton;
class SlideAnimation;
class CrossFadeAnimation;
class FlatLabel;
template <typename Widget>
class WidgetFadeWrap;
} // namespace Ui
class IntroStep;
class IntroWidget : public TWidget, public RPCSender {
namespace Intro {
class Widget : public TWidget, public RPCSender {
Q_OBJECT
public:
IntroWidget(QWidget *window);
Widget(QWidget *parent);
void animShow(const QPixmap &bgAnimCache, bool back = false);
void step_show(float64 ms, bool timer);
void stop_show();
void setInnerFocus();
void step_stage(float64 ms, bool timer);
QRect innerRect() const;
QString currentCountry() const;
enum CallStatusType {
CallWaiting,
CallCalling,
CallCalled,
CallDisabled,
};
struct CallStatus {
CallStatusType type;
int timeout;
};
void setPhone(const QString &phone, const QString &phone_hash, bool registered);
void setCode(const QString &code);
void setCallStatus(const CallStatus &status);
void setPwdSalt(const QByteArray &salt);
void setHasRecovery(bool hasRecovery);
void setPwdHint(const QString &hint);
void setCodeByTelegram(bool byTelegram);
const QString &getPhone() const;
const QString &getPhoneHash() const;
const QString &getCode() const;
const CallStatus &getCallStatus() const;
const QByteArray &getPwdSalt() const;
bool getHasRecovery() const;
const QString &getPwdHint() const;
bool codeByTelegram() const;
void finish(const MTPUser &user, const QImage &photo = QImage());
void rpcClear() override;
void langChangeTo(int32 langId);
void nextStep(IntroStep *step) {
pushStep(step, MoveForward);
}
void replaceStep(IntroStep *step) {
pushStep(step, MoveReplace);
}
~IntroWidget();
~Widget();
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
public slots:
void onStepSubmit();
void onBack();
void onChangeLang();
signals:
void countryChanged();
@@ -102,86 +57,199 @@ private slots:
void onCheckUpdateStatus();
#endif // TDESKTOP_DISABLE_AUTOUPDATE
// Internal interface.
public:
struct Data {
QString country;
QString phone;
QString phoneHash;
bool phoneIsRegistered = false;
enum class CallStatus {
Waiting,
Calling,
Called,
Disabled,
};
CallStatus callStatus = CallStatus::Disabled;
int callTimeout = 0;
QString code;
int codeLength = 5;
bool codeByTelegram = false;
QByteArray pwdSalt;
bool hasRecovery = false;
QString pwdHint;
base::Observable<void> updated;
};
enum class Direction {
Back,
Forward,
Replace,
};
class Step : public TWidget, public RPCSender {
public:
Step(QWidget *parent, Data *data, bool hasCover = false);
virtual void setInnerFocus() {
setFocus();
}
void setGoCallback(base::lambda<void(Step *step, Direction direction)> &&callback);
void setShowResetCallback(base::lambda<void()> &&callback);
void prepareShowAnimated(Step *after);
void showAnimated(Direction direction);
void showFast();
bool animating() const;
bool hasCover() const;
virtual bool hasBack() const;
virtual void activate();
virtual void cancelled();
virtual void finished();
virtual void submit() = 0;
virtual QString nextButtonText() const;
int contentLeft() const;
int contentTop() const;
void setErrorCentered(bool centered);
void setErrorBelowLink(bool below);
void showError(const QString &text);
void hideError() {
showError(QString());
}
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void setTitleText(QString richText);
void setDescriptionText(QString richText);
bool paintAnimated(Painter &p, QRect clip);
void fillSentCodeData(const MTPauth_SentCodeType &type);
void showDescription();
void hideDescription();
Data *getData() const {
return _data;
}
void finish(const MTPUser &user, QImage photo = QImage());
void goBack() {
if (_goCallback) _goCallback(nullptr, Direction::Back);
}
void goNext(Step *step) {
if (_goCallback) _goCallback(step, Direction::Forward);
}
void goReplace(Step *step) {
if (_goCallback) _goCallback(step, Direction::Replace);
}
void showResetButton() {
if (_showResetCallback) _showResetCallback();
}
private:
struct CoverAnimation {
std_::unique_ptr<Ui::CrossFadeAnimation> title;
std_::unique_ptr<Ui::CrossFadeAnimation> description;
// From content top till the next button top.
QPixmap contentSnapshotWas;
QPixmap contentSnapshotNow;
};
void updateLabelsPosition();
void paintContentSnapshot(Painter &p, const QPixmap &snapshot, float64 alpha, float64 howMuchHidden);
CoverAnimation prepareCoverAnimation(Step *step);
QPixmap prepareContentSnapshot();
QPixmap prepareSlideAnimation();
void showFinished();
void prepareCoverMask();
void paintCover(Painter &p, int top);
Data *_data = nullptr;
bool _hasCover = false;
base::lambda<void(Step *step, Direction direction)> _goCallback;
base::lambda<void()> _showResetCallback;
ChildWidget<Ui::FlatLabel> _title;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _description;
bool _errorCentered = false;
bool _errorBelowLink = false;
QString _errorText;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _error = { nullptr };
FloatAnimation _a_show;
CoverAnimation _coverAnimation;
std_::unique_ptr<Ui::SlideAnimation> _slideAnimation;
QPixmap _coverMask;
};
private:
void step_show(float64 ms, bool timer);
void changeLanguage(int32 languageId);
void updateControlsGeometry();
QPixmap grabStep(int skip = 0);
Data *getData() {
return &_data;
}
int _langChangeTo = 0;
void fixOrder();
void showControls();
void hideControls();
void moveControls();
QRect calculateStepRect() const;
Animation _a_stage;
QPixmap _cacheHide, _cacheShow;
int _cacheHideIndex = 0;
int _cacheShowIndex = 0;
anim::ivalue a_coordHide, a_coordShow;
anim::fvalue a_opacityHide, a_opacityShow;
void showResetButton();
void resetAccount();
void resetAccountSure();
void resetDone(const MTPBool &result);
bool resetFail(const RPCError &error);
Animation _a_show;
QPixmap _cacheUnder, _cacheOver;
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_shadow;
QVector<IntroStep*> _stepHistory;
IntroStep *step(int skip = 0) {
QVector<Step*> _stepHistory;
Step *getStep(int skip = 0) {
t_assert(_stepHistory.size() + skip > 0);
return _stepHistory.at(_stepHistory.size() - skip - 1);
}
enum MoveType {
MoveBack,
MoveForward,
MoveReplace,
};
void historyMove(MoveType type);
void pushStep(IntroStep *step, MoveType type);
void historyMove(Direction direction);
void moveToStep(Step *step, Direction direction);
void appendStep(Step *step);
void gotNearestDC(const MTPNearestDc &dc);
QString _countryForReg;
Data _data;
QString _phone, _phone_hash;
CallStatus _callStatus = { CallDisabled, 0 };
bool _registered = false;
QString _code;
QByteArray _pwdSalt;
bool _hasRecovery = false;
bool _codeByTelegram = false;
QString _pwdHint;
QString _firstname, _lastname;
FloatAnimation _coverShownAnimation;
int _nextTopFrom = 0;
int _controlsTopFrom = 0;
ChildWidget<Ui::WidgetFadeWrap<Ui::IconButton>> _back;
ChildWidget<Ui::RoundButton> _settings;
ChildWidget<Ui::RoundButton> _update = { nullptr };
ChildWidget<Ui::WidgetFadeWrap<Ui::RoundButton>> _update = { nullptr };
ChildWidget<Ui::WidgetFadeWrap<Ui::RoundButton>> _settings;
float64 _backFrom = 0.;
float64 _backTo = 0.;
ChildWidget<Ui::RoundButton> _next;
ChildWidget<Ui::WidgetFadeWrap<Ui::LinkButton>> _changeLanguage = { nullptr };
ChildWidget<Ui::WidgetFadeWrap<Ui::RoundButton>> _resetAccount = { nullptr };
mtpRequestId _resetRequest = 0;
};
class IntroStep : public TWidget, public RPCSender {
public:
IntroStep(IntroWidget *parent) : TWidget(parent) {
}
virtual bool hasBack() const {
return false;
}
virtual void activate() {
show();
}
virtual void cancelled() {
}
virtual void finished() {
hide();
}
virtual void onSubmit() = 0;
protected:
IntroWidget *intro() {
IntroWidget *result = qobject_cast<IntroWidget*>(parentWidget());
t_assert(result != nullptr);
return result;
}
};
} // namespace Intro