#include "LoginWindow.h" #include "RegisterDialog.h" #include "ForgotPasswordDialog.h" #include "ConfirmDialog.h" #include "UiHelpers.h" #include "TitleBar.h" #include "FramelessHelper.h" #include "UserSettings.h" #include "AppConfig.h" #include "ImClient.h" #include "Protocol.h" #include "Updater.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include LoginWindow::LoginWindow(QWidget *parent) : QWidget(parent) { setObjectName(QStringLiteral("ChromeWindow")); setWindowTitle(QStringLiteral("NIM")); setFixedSize(350, 400); setWindowFlags(Qt::Window | Qt::FramelessWindowHint); setWindowIcon(UiHelpers::appIcon()); auto *root = new QVBoxLayout(this); root->setContentsMargins(0, 0, 0, 0); root->setSpacing(0); m_titleBar = new TitleBar(this); m_titleBar->setTitle(QStringLiteral("NIM")); m_titleBar->setTheme(TitleBar::Light); m_titleBar->setMaximizable(false); m_titleBar->bind(this, false); root->addWidget(m_titleBar); m_header = UiHelpers::authHeader(QStringLiteral("即时通讯"), this); root->addWidget(m_header); m_stack = new QStackedWidget(this); m_registerPage = new RegisterDialog(m_stack); m_resetPage = new ForgotPasswordDialog(m_stack); m_stack->addWidget(buildLoginPage()); m_stack->addWidget(m_registerPage); m_stack->addWidget(m_resetPage); root->addWidget(m_stack, 1); connect(m_registerPage, &RegisterDialog::submitted, this, &LoginWindow::onRegisterSubmitted); connect(m_registerPage, &RegisterDialog::backRequested, this, [this]() { showPage(PageLogin); }); connect(m_resetPage, &ForgotPasswordDialog::submitted, this, &LoginWindow::onResetSubmitted); connect(m_resetPage, &ForgotPasswordDialog::backRequested, this, [this]() { showPage(PageLogin); }); applySavedLogin(); connect(&ImClient::instance(), &ImClient::limitsUpdated, this, &LoginWindow::applyServerLimits); applyServerLimits(); ImClient::instance().connectToServer(); tryAutoLogin(); } QWidget *LoginWindow::buildLoginPage() { auto *form = new QWidget(this); auto *lay = new QVBoxLayout(form); lay->setContentsMargins(36, 28, 36, 28); lay->setSpacing(12); m_account = UiHelpers::lineEdit(QStringLiteral("账号"), form); m_password = UiHelpers::passwordEdit(QStringLiteral("密码"), form); auto *opts = new QHBoxLayout(); opts->setContentsMargins(0, 2, 0, 0); m_remember = new QCheckBox(QStringLiteral("记住密码"), form); m_autoLogin = new QCheckBox(QStringLiteral("自动登录"), form); opts->addWidget(m_remember); opts->addStretch(); opts->addWidget(m_autoLogin); m_error = UiHelpers::errorLabel(form); m_loginBtn = UiHelpers::primaryButton(QStringLiteral("登 录"), form); m_loginBtn->setMinimumWidth(0); auto *links = new QHBoxLayout(); auto *reg = UiHelpers::linkButton(QStringLiteral("注册账号"), form); auto *forgot = UiHelpers::linkButton(QStringLiteral("忘记密码"), form); links->addWidget(reg); links->addStretch(); links->addWidget(forgot); lay->addWidget(m_account); lay->addWidget(m_password); lay->addLayout(opts); lay->addWidget(m_error); lay->addSpacing(6); lay->addWidget(m_loginBtn); lay->addLayout(links); lay->addStretch(); connect(m_loginBtn, &QPushButton::clicked, this, &LoginWindow::onLogin); connect(m_password, &QLineEdit::returnPressed, this, &LoginWindow::onLogin); connect(m_account, &QLineEdit::returnPressed, this, [this]() { m_password->setFocus(); }); connect(reg, &QPushButton::clicked, this, [this]() { showPage(PageRegister); }); connect(forgot, &QPushButton::clicked, this, [this]() { showPage(PageReset); }); connect(m_autoLogin, &QCheckBox::toggled, this, [this](bool on) { if (on) m_remember->setChecked(true); }); connect(m_remember, &QCheckBox::toggled, this, [this](bool on) { if (!on) m_autoLogin->setChecked(false); }); return form; } void LoginWindow::showPage(Page page) { m_stack->setCurrentIndex(page); switch (page) { case PageRegister: m_titleBar->setTitle(QStringLiteral("注册账号")); UiHelpers::setAuthHeaderSubtitle(m_header, QStringLiteral("创建你的账号")); m_registerPage->resetForm(); animateToHeight(580); break; case PageReset: m_titleBar->setTitle(QStringLiteral("找回密码")); UiHelpers::setAuthHeaderSubtitle(m_header, QStringLiteral("重置登录密码")); m_resetPage->resetForm(); animateToHeight(540); break; case PageLogin: default: m_titleBar->setTitle(QStringLiteral("NIM")); UiHelpers::setAuthHeaderSubtitle(m_header, QStringLiteral("即时通讯")); m_account->setFocus(); animateToHeight(400); break; } } void LoginWindow::animateToHeight(int h) { if (height() == h && minimumHeight() == h && maximumHeight() == h) return; if (m_heightAnim) { m_heightAnim->stop(); m_heightAnim = nullptr; } setMinimumWidth(350); setMaximumWidth(350); setMinimumHeight(qMin(height(), h)); setMaximumHeight(qMax(height(), h)); auto *anim = new QPropertyAnimation(this, "geometry", this); anim->setDuration(220); anim->setEasingCurve(QEasingCurve::OutCubic); const QRect from = geometry(); QRect to = from; to.setHeight(h); anim->setStartValue(from); anim->setEndValue(to); connect(anim, &QPropertyAnimation::finished, this, [this, h]() { setFixedSize(350, h); m_heightAnim = nullptr; }); m_heightAnim = anim; anim->start(QAbstractAnimation::DeleteWhenStopped); } void LoginWindow::showEvent(QShowEvent *event) { QWidget::showEvent(event); const int h = (m_stack && m_stack->currentIndex() == PageRegister) ? 580 : (m_stack && m_stack->currentIndex() == PageReset) ? 540 : 400; setFixedSize(350, h); if (m_titleBar && m_titleBar->framelessHelper()) m_titleBar->framelessHelper()->fixupSize(); QTimer::singleShot(0, this, [this, h]() { if (m_heightAnim) return; setFixedSize(350, h); if (m_titleBar && m_titleBar->framelessHelper()) m_titleBar->framelessHelper()->fixupSize(); }); } void LoginWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); QWidget::closeEvent(event); } QString LoginWindow::lastAccount() const { return m_account ? m_account->text().trimmed() : QString(); } void LoginWindow::prepareAfterLogout() { m_skipAutoLogin = true; showPage(PageLogin); } void LoginWindow::showLogoutNotice(const QString &reason) { if (reason.trimmed().isEmpty()) return; const QString text = reason; QTimer::singleShot(280, this, [this, text]() { if (!isVisible()) return; ConfirmDialog::notice(this, QStringLiteral("下线通知"), text); }); } void LoginWindow::applySavedLogin() { const auto &u = UserSettings::instance(); m_account->setText(u.lastAccount()); m_remember->setChecked(u.rememberPassword()); m_autoLogin->setChecked(u.autoLogin()); if (u.rememberPassword()) m_password->setText(u.lastPassword()); else m_password->clear(); if (u.lastAccount().isEmpty()) m_account->setFocus(); else if (!u.rememberPassword() || u.lastPassword().isEmpty()) m_password->setFocus(); } void LoginWindow::applyServerLimits() { const auto &cfg = AppConfig::instance(); if (m_account && cfg.accountMax() > 0) m_account->setMaxLength(cfg.accountMax()); if (m_password && cfg.passwordMax() > 0) m_password->setMaxLength(cfg.passwordMax()); if (m_registerPage) m_registerPage->applyLimits(); if (m_resetPage) m_resetPage->applyLimits(); Updater::promptIfNeeded(this); } void LoginWindow::tryAutoLogin() { if (m_skipAutoLogin) return; if (!willAutoLogin()) return; QTimer::singleShot(0, this, &LoginWindow::onLogin); } bool LoginWindow::willAutoLogin() const { if (m_skipAutoLogin) return false; const auto &u = UserSettings::instance(); if (!u.autoLogin() || !u.rememberPassword()) return false; if (!m_account || !m_password) return false; return !m_account->text().trimmed().isEmpty() && !m_password->text().isEmpty(); } void LoginWindow::onLogin() { if (m_loggingIn) return; UiHelpers::setError(m_error, QString()); const QString account = m_account->text().trimmed(); const QString password = m_password->text(); if (account.isEmpty() || password.isEmpty()) { UiHelpers::setError(m_error, QStringLiteral("请输入账号和密码")); return; } m_loggingIn = true; m_loginBtn->setEnabled(false); QPointer self(this); ImClient::instance().login(account, password, [self, account, password](const QJsonObject &reply) { if (!self) return; self->m_loggingIn = false; self->m_loginBtn->setEnabled(true); if (!ImClient::ok(reply)) { UiHelpers::setError(self->m_error, ImClient::messageOf(reply)); if (!self->isVisible()) { self->show(); self->raise(); self->activateWindow(); } return; } const QString nickname = reply.value(QStringLiteral("nickname")).toString(); const QString savedAccount = reply.value(QStringLiteral("account")).toString(); const QString acc = savedAccount.isEmpty() ? account : savedAccount; UserSettings::instance().saveLogin(acc, nickname.isEmpty() ? acc : nickname, password, self->m_remember->isChecked(), self->m_autoLogin->isChecked()); emit self->loginSucceeded(acc, nickname.isEmpty() ? acc : nickname); }); } void LoginWindow::onRegisterSubmitted(qint64 /*uid*/) { m_account->setText(m_registerPage->account()); m_password->clear(); UiHelpers::setError(m_error, QString()); showPage(PageLogin); m_password->setFocus(); UiHelpers::showToast(this, QStringLiteral("注册成功,请使用账号登录")); } void LoginWindow::onResetSubmitted() { m_password->clear(); UiHelpers::setError(m_error, QString()); showPage(PageLogin); m_password->setFocus(); UiHelpers::showToast(this, QStringLiteral("密码已重置,请登录")); }