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)
};
if (IsForceLogoutNotification(d)) {
Core::App().forceLogOut(text);
Core::App().forceLogOut(&session().account(), text);
} else if (d.is_popup()) {
const auto &windows = session().windows();
if (!windows.empty()) {

View File

@ -451,15 +451,33 @@ void Application::startLocalStorage() {
_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>(
explanation,
tr::lng_passcode_logout(tr::now)));
box->setCloseByEscape(false);
box->setCloseByOutsideClick(false);
const auto weak = base::make_weak(account.get());
connect(box, &QObject::destroyed, [=] {
crl::on_main(this, [=] {
activeAccount().forcedLogOut();
crl::on_main(weak, [=] {
account->forcedLogOut();
});
});
}

View File

@ -189,7 +189,10 @@ public:
bool openLocalUrl(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 lockByPasscode();
void unlockPasscode();

View File

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

View File

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

View File

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

View File

@ -30,34 +30,51 @@ bool Accounts::started() const {
Storage::StartResult Accounts::start(const QByteArray &passcode) {
Expects(!started());
auto active = -1;
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);
const auto result = _local->start(passcode);
if (result == Storage::StartResult::Success) {
Assert(started());
for (const auto &[index, account] : _accounts) {
account->startMtp();
}
if (Local::oldSettingsVersion() < AppVersion) {
Local::writeSettings();
}
activate(active);
activateAfterStarting();
} else {
Assert(!started());
}
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 {
return _accounts;
}
@ -97,8 +114,6 @@ rpl::producer<Session*> Accounts::activeSessionValue() const {
}
int Accounts::add() {
Expects(!Core::App().locked());
auto index = 0;
while (_accounts.contains(index)) {
++index;
@ -108,10 +123,57 @@ int Accounts::add() {
std::make_unique<Account>(_dataName, index)
).first->second.get();
_local->startAdded(account);
account->startMtp();
watchSession(account);
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) {
Expects(_accounts.contains(index));
@ -121,7 +183,16 @@ void Accounts::activate(int index) {
_active.current()->sessionValue(
) | rpl::start_to_stream(_activeSessions, _activeLifetime);
scheduleWriteAccounts();
}
void Accounts::scheduleWriteAccounts() {
if (_writeAccountsScheduled) {
return;
}
_writeAccountsScheduled = true;
crl::on_main(&Core::App(), [=] {
_writeAccountsScheduled = false;
_local->writeAccounts();
});
}

View File

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

View File

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

View File

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

View File

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

View File

@ -756,10 +756,10 @@ void MainWindow::createGlobalMenu() {
auto 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();
});
auto quit = file->addAction(
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);
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));
psUndo = edit->addAction(tr::lng_mac_menu_undo(tr::now), this, SLOT(psMacUndo()), QKeySequence::Undo);

View File

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

View File

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

View File

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

View File

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