Support logout of a secondary account.

This commit is contained in:
John Preston 2020-06-16 13:40:43 +04:00
parent 5e045ec02c
commit bc144377c0
18 changed files with 189 additions and 81 deletions

View file

@ -1722,7 +1722,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
Api::EntitiesFromMTP(&session(), d.ventities().v) Api::EntitiesFromMTP(&session(), d.ventities().v)
}; };
if (IsForceLogoutNotification(d)) { if (IsForceLogoutNotification(d)) {
Core::App().forceLogOut(text); Core::App().forceLogOut(&session().account(), text);
} else if (d.is_popup()) { } else if (d.is_popup()) {
const auto &windows = session().windows(); const auto &windows = session().windows();
if (!windows.empty()) { if (!windows.empty()) {

View file

@ -451,15 +451,33 @@ void Application::startLocalStorage() {
_saveSettingsTimer.setCallback([=] { Local::writeSettings(); }); _saveSettingsTimer.setCallback([=] { Local::writeSettings(); });
} }
void Application::forceLogOut(const TextWithEntities &explanation) { void Application::logout(Main::Account *account) {
if (account) {
account->logOut();
} else {
accounts().resetWithForgottenPasscode();
if (Global::LocalPasscode()) {
Global::SetLocalPasscode(false);
Global::RefLocalPasscodeChanged().notify();
}
Core::App().unlockPasscode();
Core::App().unlockTerms();
}
}
void Application::forceLogOut(
not_null<Main::Account*> account,
const TextWithEntities &explanation) {
const auto box = Ui::show(Box<InformBox>( const auto box = Ui::show(Box<InformBox>(
explanation, explanation,
tr::lng_passcode_logout(tr::now))); tr::lng_passcode_logout(tr::now)));
box->setCloseByEscape(false); box->setCloseByEscape(false);
box->setCloseByOutsideClick(false); box->setCloseByOutsideClick(false);
const auto weak = base::make_weak(account.get());
connect(box, &QObject::destroyed, [=] { connect(box, &QObject::destroyed, [=] {
crl::on_main(this, [=] { crl::on_main(weak, [=] {
activeAccount().forcedLogOut(); account->forcedLogOut();
}); });
}); });
} }

View file

@ -189,7 +189,10 @@ public:
bool openLocalUrl(const QString &url, QVariant context); bool openLocalUrl(const QString &url, QVariant context);
bool openInternalUrl(const QString &url, QVariant context); bool openInternalUrl(const QString &url, QVariant context);
void forceLogOut(const TextWithEntities &explanation); void logout(Main::Account *account = nullptr);
void forceLogOut(
not_null<Main::Account*> account,
const TextWithEntities &explanation);
void checkLocalTime(); void checkLocalTime();
void lockByPasscode(); void lockByPasscode();
void unlockPasscode(); void unlockPasscode();

View file

@ -1118,8 +1118,7 @@ void Session::setupUserIsContactViewer() {
Session::~Session() { Session::~Session() {
// Optimization: clear notifications before destroying items. // Optimization: clear notifications before destroying items.
_session->notifications().clearAllFast(); _session->notifications().clearAllFast();
clearLocalStorage();
clear();
} }
template <typename Method> template <typename Method>
@ -3840,6 +3839,8 @@ void Session::clearLocalStorage() {
_cache->close(); _cache->close();
_cache->clear(); _cache->clear();
_bigFileCache->close();
_bigFileCache->clear();
} }
} // namespace Data } // namespace Data

View file

@ -170,7 +170,6 @@ void Account::destroySession() {
if (!sessionExists()) { if (!sessionExists()) {
return; return;
} }
session().data().clear();
_sessionValue = nullptr; _sessionValue = nullptr;
_session = nullptr; _session = nullptr;
@ -442,6 +441,10 @@ void Account::logOut() {
} }
} }
bool Account::loggingOut() const {
return _loggingOut;
}
void Account::forcedLogOut() { void Account::forcedLogOut() {
if (sessionExists()) { if (sessionExists()) {
resetAuthorizationKeys(); resetAuthorizationKeys();
@ -451,20 +454,10 @@ void Account::forcedLogOut() {
void Account::loggedOut() { void Account::loggedOut() {
_loggingOut = false; _loggingOut = false;
if (Global::LocalPasscode()) {
Global::SetLocalPasscode(false);
Global::RefLocalPasscodeChanged().notify();
}
Core::App().unlockPasscode();
Core::App().unlockTerms();
Media::Player::mixer()->stopAndClear(); Media::Player::mixer()->stopAndClear();
Global::SetVoiceMsgPlaybackDoubled(false); Global::SetVoiceMsgPlaybackDoubled(false);
if (const auto window = Core::App().activeWindow()) { if (const auto window = Core::App().activeWindow()) {
window->tempDirDelete(Local::ClearManagerAll); window->tempDirDelete(Local::ClearManagerAll);
window->setupIntro();
}
if (sessionExists()) {
session().data().clearLocalStorage();
} }
destroySession(); destroySession();
local().reset(); local().reset();

View file

@ -50,6 +50,7 @@ public:
void logOut(); void logOut();
void forcedLogOut(); void forcedLogOut();
[[nodiscard]] bool loggingOut() const;
[[nodiscard]] AppConfig &appConfig() const { [[nodiscard]] AppConfig &appConfig() const {
Expects(_appConfig != nullptr); Expects(_appConfig != nullptr);
@ -95,6 +96,10 @@ public:
[[nodiscard]] rpl::producer<> configUpdates() const; [[nodiscard]] rpl::producer<> configUpdates() const;
void clearMtp(); void clearMtp();
rpl::lifetime &lifetime() {
return _lifetime;
}
private: private:
void createSession( void createSession(
const MTPUser &user, const MTPUser &user,

View file

@ -30,34 +30,51 @@ bool Accounts::started() const {
Storage::StartResult Accounts::start(const QByteArray &passcode) { Storage::StartResult Accounts::start(const QByteArray &passcode) {
Expects(!started()); Expects(!started());
auto active = -1; const auto result = _local->start(passcode);
const auto callback = [&](int index, std::unique_ptr<Account> account) {
Expects(account != nullptr);
Expects(!_accounts.contains(index));
if (_accounts.empty()) {
active = index;
}
_accounts.emplace(index, std::move(account));
};
const auto result = _local->start(passcode, callback);
if (result == Storage::StartResult::Success) { if (result == Storage::StartResult::Success) {
Assert(started());
for (const auto &[index, account] : _accounts) {
account->startMtp();
}
if (Local::oldSettingsVersion() < AppVersion) { if (Local::oldSettingsVersion() < AppVersion) {
Local::writeSettings(); Local::writeSettings();
} }
activateAfterStarting();
activate(active);
} else { } else {
Assert(!started()); Assert(!started());
} }
return result; return result;
} }
void Accounts::accountAddedInStorage(
int index,
std::unique_ptr<Account> account) {
Expects(account != nullptr);
Expects(!_accounts.contains(index));
if (_accounts.empty()) {
_activeIndex = index;
}
_accounts.emplace(index, std::move(account));
};
void Accounts::resetWithForgottenPasscode() {
if (_accounts.empty()) {
_local->startFromScratch();
activateAfterStarting();
} else {
for (const auto &[index, account] : _accounts) {
account->logOut();
}
}
}
void Accounts::activateAfterStarting() {
Expects(started());
for (const auto &[index, account] : _accounts) {
watchSession(account.get());
}
activate(_activeIndex);
}
const base::flat_map<int, std::unique_ptr<Account>> &Accounts::list() const { const base::flat_map<int, std::unique_ptr<Account>> &Accounts::list() const {
return _accounts; return _accounts;
} }
@ -97,8 +114,6 @@ rpl::producer<Session*> Accounts::activeSessionValue() const {
} }
int Accounts::add() { int Accounts::add() {
Expects(!Core::App().locked());
auto index = 0; auto index = 0;
while (_accounts.contains(index)) { while (_accounts.contains(index)) {
++index; ++index;
@ -108,10 +123,57 @@ int Accounts::add() {
std::make_unique<Account>(_dataName, index) std::make_unique<Account>(_dataName, index)
).first->second.get(); ).first->second.get();
_local->startAdded(account); _local->startAdded(account);
account->startMtp(); watchSession(account);
return index; return index;
} }
void Accounts::watchSession(not_null<Account*> account) {
account->startMtp();
account->sessionChanges(
) | rpl::filter([=](Session *session) {
return !session && _accounts.size() > 1;
}) | rpl::start_with_next([=](Session *session) {
if (account == _active.current()) {
activateAuthedAccount();
}
crl::on_main(&Core::App(), [=] {
removeRedundantAccounts();
});
}, account->lifetime());
}
void Accounts::activateAuthedAccount() {
Expects(started());
if (_active.current()->sessionExists()) {
return;
}
for (auto i = _accounts.begin(); i != _accounts.end(); ++i) {
if (i->second->sessionExists()) {
activate(i->first);
return;
}
}
}
void Accounts::removeRedundantAccounts() {
Expects(started());
const auto was = _accounts.size();
activateAuthedAccount();
for (auto i = _accounts.begin(); i != _accounts.end();) {
if (i->second.get() == _active.current()
|| i->second->sessionExists()) {
++i;
continue;
}
i = _accounts.erase(i);
}
if (_accounts.size() != was) {
scheduleWriteAccounts();
}
}
void Accounts::activate(int index) { void Accounts::activate(int index) {
Expects(_accounts.contains(index)); Expects(_accounts.contains(index));
@ -121,7 +183,16 @@ void Accounts::activate(int index) {
_active.current()->sessionValue( _active.current()->sessionValue(
) | rpl::start_to_stream(_activeSessions, _activeLifetime); ) | rpl::start_to_stream(_activeSessions, _activeLifetime);
scheduleWriteAccounts();
}
void Accounts::scheduleWriteAccounts() {
if (_writeAccountsScheduled) {
return;
}
_writeAccountsScheduled = true;
crl::on_main(&Core::App(), [=] { crl::on_main(&Core::App(), [=] {
_writeAccountsScheduled = false;
_local->writeAccounts(); _local->writeAccounts();
}); });
} }

View file

@ -23,7 +23,8 @@ public:
~Accounts(); ~Accounts();
[[nodiscard]] bool started() const; [[nodiscard]] bool started() const;
Storage::StartResult start(const QByteArray &passcode); [[nodiscard]] Storage::StartResult start(const QByteArray &passcode);
void resetWithForgottenPasscode();
[[nodiscard]] Storage::Accounts &local() const { [[nodiscard]] Storage::Accounts &local() const {
return *_local; return *_local;
@ -44,13 +45,23 @@ public:
[[nodiscard]] int add(); [[nodiscard]] int add();
void activate(int index); void activate(int index);
// Interface for Storage::Accounts.
void accountAddedInStorage(int index, std::unique_ptr<Account> account);
private: private:
void activateAfterStarting();
void activateAuthedAccount();
void removeRedundantAccounts();
void watchSession(not_null<Account*> account);
void scheduleWriteAccounts();
const QString _dataName; const QString _dataName;
const std::unique_ptr<Storage::Accounts> _local; const std::unique_ptr<Storage::Accounts> _local;
base::flat_map<int, std::unique_ptr<Account>> _accounts; base::flat_map<int, std::unique_ptr<Account>> _accounts;
rpl::variable<Account*> _active = nullptr; rpl::variable<Account*> _active = nullptr;
int _activeIndex = 0; int _activeIndex = 0;
bool _writeAccountsScheduled = false;
rpl::event_stream<Session*> _activeSessions; rpl::event_stream<Session*> _activeSessions;

View file

@ -114,6 +114,7 @@ Session::Session(
} }
Session::~Session() { Session::~Session() {
data().clear();
ClickHandler::clearActive(); ClickHandler::clearActive();
ClickHandler::unpressed(); ClickHandler::unpressed();
} }

View file

@ -652,22 +652,29 @@ void MainWindow::onShowNewChannel() {
} }
} }
void MainWindow::onLogout() { void MainWindow::showLogoutConfirmation() {
if (isHidden()) { if (isHidden()) {
showFromTray(); showFromTray();
} }
const auto callback = [=] { const auto account = Core::App().passcodeLocked()
if (account().sessionExists() ? nullptr
&& account().session().data().exportInProgress()) { : sessionController()
? &sessionController()->session().account()
: nullptr;
const auto weak = base::make_weak(account);
const auto callback = crl::guard(weak, [=] {
if (account
&& account->sessionExists()
&& account->session().data().exportInProgress()) {
Ui::hideLayer(); Ui::hideLayer();
account().session().data().stopExportWithConfirmation([=] { account->session().data().stopExportWithConfirmation([=] {
account().logOut(); Core::App().logout(account);
}); });
} else { } else {
account().logOut(); Core::App().logout(account);
} }
}; });
Ui::show(Box<ConfirmBox>( Ui::show(Box<ConfirmBox>(
tr::lng_sure_logout(tr::now), tr::lng_sure_logout(tr::now),
tr::lng_settings_logout(tr::now), tr::lng_settings_logout(tr::now),

View file

@ -113,6 +113,8 @@ public:
not_null<PhotoData*> photo); not_null<PhotoData*> photo);
void hideMediaPreview(); void hideMediaPreview();
void showLogoutConfirmation();
void updateControlsGeometry() override; void updateControlsGeometry() override;
protected: protected:
@ -137,7 +139,6 @@ public slots:
void onShowAddContact(); void onShowAddContact();
void onShowNewGroup(); void onShowNewGroup();
void onShowNewChannel(); void onShowNewChannel();
void onLogout();
signals: signals:
void tempDirCleared(int task); void tempDirCleared(int task);

View file

@ -756,10 +756,10 @@ void MainWindow::createGlobalMenu() {
auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now)); auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now));
psLogout = file->addAction( psLogout = file->addAction(tr::lng_mac_menu_logout(tr::now));
tr::lng_mac_menu_logout(tr::now), connect(psLogout, &QAction::triggered, psLogout, [] {
App::wnd(), if (App::wnd()) App::wnd()->showLogoutConfirmation();
SLOT(onLogout())); });
auto quit = file->addAction( auto quit = file->addAction(
tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, qsl("Telegram")), tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, qsl("Telegram")),

View file

@ -681,7 +681,10 @@ void MainWindow::createGlobalMenu() {
prefs->setMenuRole(QAction::PreferencesRole); prefs->setMenuRole(QAction::PreferencesRole);
QMenu *file = psMainMenu.addMenu(tr::lng_mac_menu_file(tr::now)); QMenu *file = psMainMenu.addMenu(tr::lng_mac_menu_file(tr::now));
psLogout = file->addAction(tr::lng_mac_menu_logout(tr::now), App::wnd(), SLOT(onLogout())); psLogout = file->addAction(tr::lng_mac_menu_logout(tr::now));
connect(psLogout, &QAction::triggered, psLogout, [] {
if (App::wnd()) App::wnd()->showLogoutConfirmation();
});
QMenu *edit = psMainMenu.addMenu(tr::lng_mac_menu_edit(tr::now)); QMenu *edit = psMainMenu.addMenu(tr::lng_mac_menu_edit(tr::now));
psUndo = edit->addAction(tr::lng_mac_menu_undo(tr::now), this, SLOT(psMacUndo()), QKeySequence::Undo); psUndo = edit->addAction(tr::lng_mac_menu_undo(tr::now), this, SLOT(psMacUndo()), QKeySequence::Undo);

View file

@ -202,7 +202,7 @@ void FillMenu(
} }
addAction( addAction(
tr::lng_settings_logout(tr::now), tr::lng_settings_logout(tr::now),
[=] { window->widget()->onLogout(); }); [=] { window->widget()->showLogoutConfirmation(); });
} }
} }

View file

@ -41,10 +41,8 @@ Accounts::Accounts(not_null<Main::Accounts*> owner, const QString &dataName)
Accounts::~Accounts() = default; Accounts::~Accounts() = default;
StartResult Accounts::start( StartResult Accounts::start(const QByteArray &passcode) {
const QByteArray &passcode, const auto modern = startModern(passcode);
Fn<void(int, std::unique_ptr<Main::Account>)> callback) {
const auto modern = startModern(passcode, callback);
if (modern == StartModernResult::Success) { if (modern == StartModernResult::Success) {
if (_oldVersion < AppVersion) { if (_oldVersion < AppVersion) {
writeAccounts(); writeAccounts();
@ -53,20 +51,14 @@ StartResult Accounts::start(
} else if (modern == StartModernResult::IncorrectPasscode) { } else if (modern == StartModernResult::IncorrectPasscode) {
return StartResult::IncorrectPasscode; return StartResult::IncorrectPasscode;
} else if (modern == StartModernResult::Failed) { } else if (modern == StartModernResult::Failed) {
startWithSingleAccount( startFromScratch();
passcode,
std::move(callback),
std::make_unique<Main::Account>(_dataName, 0));
return StartResult::Success; return StartResult::Success;
} }
auto legacy = std::make_unique<Main::Account>(_dataName, 0); auto legacy = std::make_unique<Main::Account>(_dataName, 0);
const auto result = legacy->legacyStart(passcode); const auto result = legacy->legacyStart(passcode);
if (result == StartResult::Success) { if (result == StartResult::Success) {
_oldVersion = legacy->local().oldMapVersion(); _oldVersion = legacy->local().oldMapVersion();
startWithSingleAccount( startWithSingleAccount(passcode, std::move(legacy));
passcode,
std::move(callback),
std::move(legacy));
} }
return result; return result;
} }
@ -79,7 +71,6 @@ void Accounts::startAdded(not_null<Main::Account*> account) {
void Accounts::startWithSingleAccount( void Accounts::startWithSingleAccount(
const QByteArray &passcode, const QByteArray &passcode,
Fn<void(int, std::unique_ptr<Main::Account>)> callback,
std::unique_ptr<Main::Account> account) { std::unique_ptr<Main::Account> account) {
Expects(account != nullptr); Expects(account != nullptr);
@ -90,7 +81,7 @@ void Accounts::startWithSingleAccount(
generateLocalKey(); generateLocalKey();
account->start(_localKey); account->start(_localKey);
} }
callback(0, std::move(account)); _owner->accountAddedInStorage(0, std::move(account));
writeAccounts(); writeAccounts();
} }
@ -119,8 +110,7 @@ void Accounts::encryptLocalKey(const QByteArray &passcode) {
} }
Accounts::StartModernResult Accounts::startModern( Accounts::StartModernResult Accounts::startModern(
const QByteArray &passcode, const QByteArray &passcode) {
Fn<void(int, std::unique_ptr<Main::Account>)> callback) {
const auto name = ComputeKeyName(_dataName); const auto name = ComputeKeyName(_dataName);
FileReadDescriptor keyData; FileReadDescriptor keyData;
@ -185,7 +175,7 @@ Accounts::StartModernResult Accounts::startModern(
const auto userId = account->willHaveUserId(); const auto userId = account->willHaveUserId();
if (!users.contains(userId) if (!users.contains(userId)
&& (userId != 0 || (users.empty() && i + 1 == count))) { && (userId != 0 || (users.empty() && i + 1 == count))) {
callback(index, std::move(account)); _owner->accountAddedInStorage(index, std::move(account));
users.emplace(userId); users.emplace(userId);
} }
} }
@ -223,6 +213,12 @@ void Accounts::writeAccounts() {
key.writeEncrypted(keyData, _localKey); key.writeEncrypted(keyData, _localKey);
} }
void Accounts::startFromScratch() {
startWithSingleAccount(
QByteArray(),
std::make_unique<Main::Account>(_dataName, 0));
}
bool Accounts::checkPasscode(const QByteArray &passcode) const { bool Accounts::checkPasscode(const QByteArray &passcode) const {
Expects(!_passcodeKeySalt.isEmpty()); Expects(!_passcodeKeySalt.isEmpty());
Expects(_passcodeKey != nullptr); Expects(_passcodeKey != nullptr);

View file

@ -29,11 +29,10 @@ public:
Accounts(not_null<Main::Accounts*> owner, const QString &dataName); Accounts(not_null<Main::Accounts*> owner, const QString &dataName);
~Accounts(); ~Accounts();
[[nodiscard]] StartResult start( [[nodiscard]] StartResult start(const QByteArray &passcode);
const QByteArray &passcode,
Fn<void(int, std::unique_ptr<Main::Account>)> callback);
void startAdded(not_null<Main::Account*> account); void startAdded(not_null<Main::Account*> account);
void writeAccounts(); void writeAccounts();
void startFromScratch();
[[nodiscard]] bool checkPasscode(const QByteArray &passcode) const; [[nodiscard]] bool checkPasscode(const QByteArray &passcode) const;
void setPasscode(const QByteArray &passcode); void setPasscode(const QByteArray &passcode);
@ -49,12 +48,9 @@ private:
Empty, Empty,
}; };
[[nodiscard]] StartModernResult startModern( [[nodiscard]] StartModernResult startModern(const QByteArray &passcode);
const QByteArray &passcode,
Fn<void(int, std::unique_ptr<Main::Account>)> callback);
void startWithSingleAccount( void startWithSingleAccount(
const QByteArray &passcode, const QByteArray &passcode,
Fn<void(int, std::unique_ptr<Main::Account>)> callback,
std::unique_ptr<Main::Account> account); std::unique_ptr<Main::Account> account);
void generateLocalKey(); void generateLocalKey();
void encryptLocalKey(const QByteArray &passcode); void encryptLocalKey(const QByteArray &passcode);

View file

@ -68,7 +68,7 @@ void Controller::showAccount(not_null<Main::Account*> account) {
} else { } else {
setupIntro(); setupIntro();
} }
}, _lifetime); }, _accountLifetime);
} }
void Controller::finishFirstShow() { void Controller::finishFirstShow() {

View file

@ -117,7 +117,9 @@ PasscodeLockWidget::PasscodeLockWidget(
connect(_passcode, &Ui::MaskedInputField::submitted, [=] { submit(); }); connect(_passcode, &Ui::MaskedInputField::submitted, [=] { submit(); });
_submit->setClickedCallback([=] { submit(); }); _submit->setClickedCallback([=] { submit(); });
_logout->setClickedCallback([=] { window->widget()->onLogout(); }); _logout->setClickedCallback([=] {
window->widget()->showLogoutConfirmation();
});
} }
void PasscodeLockWidget::paintContent(Painter &p) { void PasscodeLockWidget::paintContent(Painter &p) {