Skip to content

Commit

Permalink
Multiple Users #3 - Initial work to list and switch between accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
SneWs committed Jul 12, 2024
1 parent 1fe45a4 commit 86e7c22
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 15 deletions.
32 changes: 23 additions & 9 deletions AccountsTabUiManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,45 @@ AccountsTabUiManager::AccountsTabUiManager(Ui::MainWindow* u, TailRunner* runner
wnd->hide();
}
);

connect(ui->btnAddAccount, &QPushButton::clicked, this, [this]() {
pTailRunner->login();
}
);

connect(ui->lstAccounts, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) {
// TODO: Get info about selected account
auto accountId = item->data(Qt::UserRole).toString();
qDebug() << "Selected account: " << accountId;
}
);
}

AccountsTabUiManager::~AccountsTabUiManager() {
}

void AccountsTabUiManager::onAccountsListed(const QList<TailAccountInfo>& foundAccounts) {
accounts = foundAccounts;
ui->lstAccounts->clear();
for (const auto& account : foundAccounts) {
auto* pCurrent = new QListWidgetItem(account.tailnet + "\n" + account.account);
pCurrent->setData(Qt::UserRole, account.id);
ui->lstAccounts->addItem(pCurrent);
pCurrent->setTextAlignment(Qt::AlignmentFlag::AlignLeft | Qt::AlignmentFlag::AlignVCenter);
}
}

void AccountsTabUiManager::onTailStatusChanged(TailStatus* pTailStatus) {
if (pTailStatus->user->id <= 0) {
// Not logged in

// Hide account details view, nothing to show
ui->accountDetailsContainer->setVisible(false);
ui->lstAccounts->clear();
return;
}

// Show account details view
ui->accountDetailsContainer->setVisible(true);
ui->lstAccounts->clear();
auto* pCurrent = new QListWidgetItem(pTailStatus->user->displayName + "\n" +
pTailStatus->user->loginName);
ui->lstAccounts->addItem(pCurrent);

pCurrent->setSelected(true);
pCurrent->setTextAlignment(Qt::AlignmentFlag::AlignLeft | Qt::AlignmentFlag::AlignVCenter);

ui->lblUsername->setText(pTailStatus->user->displayName);
ui->lblTailnetName->setText(pTailStatus->user->loginName);
ui->lblEmail->setText(pTailStatus->user->loginName);
Expand Down
2 changes: 2 additions & 0 deletions AccountsTabUiManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ class AccountsTabUiManager : public QObject
explicit AccountsTabUiManager(Ui::MainWindow* ui, TailRunner* runner, QObject* parent = nullptr);
virtual ~AccountsTabUiManager();

void onAccountsListed(const QList<TailAccountInfo>& foundAccounts);
void onTailStatusChanged(TailStatus* pTailStatus);

private:
Ui::MainWindow* ui;
TailRunner* pTailRunner;
QList<TailAccountInfo> accounts;
};


Expand Down
12 changes: 11 additions & 1 deletion MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ MainWindow::MainWindow(QWidget* parent)
pCurrentExecution = new TailRunner(settings, this);
connect(pCurrentExecution, &TailRunner::statusUpdated, this, &MainWindow::onTailStatusChanged);
connect(pCurrentExecution, &TailRunner::loginFlowCompleted, this, &MainWindow::loginFlowCompleted);
connect(pCurrentExecution, &TailRunner::accountsListed, this, &MainWindow::onAccountsListed);

accountsTabUi = new AccountsTabUiManager(ui, pCurrentExecution, this);
pTrayManager = new TrayMenuManager(settings, pCurrentExecution, this);

changeToState(TailState::NotLoggedIn);
pCurrentExecution->checkStatus();
pCurrentExecution->getAccounts();

connect(ui->btnSettingsClose, &QPushButton::clicked,
this, &MainWindow::settingsClosed);
Expand Down Expand Up @@ -58,6 +59,15 @@ void MainWindow::showAboutTab() {
show();
}

void MainWindow::onAccountsListed(const QList<TailAccountInfo>& foundAccounts) {
accounts = foundAccounts;
pTrayManager->onAccountsListed(foundAccounts);
pTrayManager->stateChangedTo(eCurrentState, pTailStatus);

accountsTabUi->onAccountsListed(foundAccounts);
accountsTabUi->onTailStatusChanged(pTailStatus);
}

void MainWindow::settingsClosed() {
syncSettingsFromUi();
pCurrentExecution->start();
Expand Down
5 changes: 3 additions & 2 deletions MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ class MainWindow : public QMainWindow
Ui::MainWindow* ui;
AccountsTabUiManager* accountsTabUi;
TrayMenuManager* pTrayManager;

TailState eCurrentState;
TailRunner* pCurrentExecution;
TailStatus* pTailStatus;

TailState eCurrentState;
TailSettings settings;
QList<TailAccountInfo> accounts;

private slots:
void onAccountsListed(const QList<TailAccountInfo>& foundAccounts);
void settingsClosed();
void loginFlowCompleted();

Expand Down
41 changes: 39 additions & 2 deletions TailRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ void TailRunner::checkStatus() {
runCommand("status", QStringList(), true);
}

void TailRunner::getAccounts() {
eCommand = Command::ListAccounts;
QStringList args;
args << "--list";
runCommand("switch", args);
}

void TailRunner::switchAccount(const QString& accountId) {
eCommand = Command::SwitchAccount;
QStringList args;
args << accountId;
runCommand("switch", args);
}

void TailRunner::login() {
eCommand = Command::Login;
QStringList args;
Expand Down Expand Up @@ -122,8 +136,13 @@ void TailRunner::runCommand(QString cmd, QStringList args, bool jsonResult, bool
}
}
else {
// After we've invoked a command not status command we check for new status update
if (eCommand != Command::Status && eCommand != Command::Logout) {
if (eCommand == Command::SwitchAccount) {
getAccounts();
}
else if (eCommand == Command::ListAccounts) {
checkStatus();
}
else if (eCommand != Command::Status && eCommand != Command::Logout) {
QTimer::singleShot(1000, this, [this]() {
checkStatus();
});
Expand Down Expand Up @@ -217,6 +236,24 @@ void TailRunner::onProcessCanReadStdOut() {
parseStatusResponse(obj);
break;
}
case Command::ListAccounts: {
QList<TailAccountInfo> accounts;
const QString raw(data);
const auto lines = raw.split('\n', Qt::SkipEmptyParts);
for (const auto& line : lines) {
qDebug() << line;
auto accountInfo = TailAccountInfo::parse(line);
if (accountInfo.id.length() > 3) {
if (accountInfo.account.endsWith('*'))
accounts.insert(0, accountInfo);
else
accounts.emplace_back(accountInfo);
}
}

emit accountsListed(accounts);
break;
}
case Command::Connect: {
// Will be a simple json string with backend state
QJsonParseError* parseError = nullptr;
Expand Down
5 changes: 5 additions & 0 deletions TailRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class TailRunner : public QObject
virtual ~TailRunner();

void checkStatus();
void getAccounts();
void switchAccount(const QString& accountId);

void login();
void logout();
Expand All @@ -28,6 +30,8 @@ class TailRunner : public QObject
const TailSettings& settings;
QProcess* pProcess;
enum class Command {
ListAccounts,
SwitchAccount,
Login,
Logout,
Connect,
Expand All @@ -39,6 +43,7 @@ class TailRunner : public QObject
Command eCommand;

signals:
void accountsListed(const QList<TailAccountInfo>& accounts);
void statusUpdated(TailStatus* newStatus);
void loginFlowCompleted();

Expand Down
26 changes: 25 additions & 1 deletion TrayMenuManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

TrayMenuManager::TrayMenuManager(TailSettings& s, TailRunner* runner, QObject* parent)
: QObject(parent)
, accounts()
, settings(s)
, pTailRunner(runner)
, pStatusCheckTimer(nullptr)
Expand Down Expand Up @@ -82,6 +83,10 @@ TrayMenuManager::~TrayMenuManager()
delete pThisDevice;
}

void TrayMenuManager::onAccountsListed(const QList<TailAccountInfo>& foundAccounts) {
accounts = foundAccounts;
}

void TrayMenuManager::stateChangedTo(TailState newState, TailStatus const* pTailStatus)
{
switch (newState) {
Expand Down Expand Up @@ -137,6 +142,8 @@ void TrayMenuManager::buildNotConnectedMenu(TailStatus const* pTailStatus)
pTrayMenu->addAction(pQuitAction);

pSysTray->setIcon(QIcon(":/icons/tray-off.png"));

buildAccountsMenu();
}

void TrayMenuManager::buildConnectedMenu(TailStatus const* pTailStatus)
Expand All @@ -149,7 +156,8 @@ void TrayMenuManager::buildConnectedMenu(TailStatus const* pTailStatus)
pTrayMenu->addAction(pThisDevice);

pTrayMenu->addSeparator();
pTrayMenu->addAction("This device: " + pTailStatus->self->hostName);
pThisDevice->setText(pTailStatus->user->loginName);
pTrayMenu->addAction(pThisDevice);

auto* netDevs = pTrayMenu->addMenu("Network devices");
for (auto* dev : pTailStatus->peers) {
Expand Down Expand Up @@ -209,13 +217,29 @@ void TrayMenuManager::buildConnectedMenu(TailStatus const* pTailStatus)
pTrayMenu->addAction(pQuitAction);

pSysTray->setIcon(QIcon(":/icons/tray-on.png"));
buildAccountsMenu();
}

void TrayMenuManager::buildConnectedExitNodeMenu(TailStatus const* pTailStatus)
{

}

void TrayMenuManager::buildAccountsMenu() {
if (pThisDevice->menu() == nullptr) {
pThisDevice->setMenu(new QMenu());
}
pThisDevice->menu()->clear();
for (const auto& acc : accounts) {
auto accountAction = new QAction(acc.tailnet + " (" + acc.account + ")");
pThisDevice->menu()->addAction(accountAction);
accountAction->setData(acc.id);
connect(accountAction, &QAction::triggered, this, [this, accountAction](bool) {
pTailRunner->switchAccount(accountAction->data().toString());
});
}
}

void TrayMenuManager::setupWellKnownActions() {
connect(pLoginAction, &QAction::triggered, this, [this](bool) {
pTailRunner->login();
Expand Down
4 changes: 4 additions & 0 deletions TrayMenuManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ class TrayMenuManager : public QObject
explicit TrayMenuManager(TailSettings& s, TailRunner* runner, QObject* parent = nullptr);
virtual ~TrayMenuManager();

void onAccountsListed(const QList<TailAccountInfo>& foundAccounts);
void stateChangedTo(TailState newState, TailStatus const* pTailStatus);

QSystemTrayIcon* trayIcon() const { return pSysTray; }

private:
QList<TailAccountInfo> accounts;
TailSettings& settings;
TailRunner* pTailRunner;
QTimer* pStatusCheckTimer;
Expand All @@ -46,6 +48,8 @@ class TrayMenuManager : public QObject
void buildConnectedMenu(TailStatus const* pTailStatus);
void buildConnectedExitNodeMenu(TailStatus const* pTailStatus);

void buildAccountsMenu();

void setupWellKnownActions();
};

Expand Down
47 changes: 47 additions & 0 deletions models.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,53 @@ enum class TailState {
ConnectedWithExitNode
};

class TailAccountInfo : public QObject
{
Q_OBJECT
public:
QString id;
QString tailnet;
QString account;

TailAccountInfo()
: QObject(nullptr)
, id("")
, tailnet("")
, account("")
{ }

TailAccountInfo(const TailAccountInfo& o)
: QObject(nullptr)
, id(o.id)
, tailnet(o.tailnet)
, account(o.account)
{ }

TailAccountInfo& operator = (const TailAccountInfo& o) {
id = o.id;
tailnet = o.tailnet;
account = o.account;

return *this;
}

static TailAccountInfo parse(const QString& rawLineData) {
// NOTE: This is a raw line from the command output
// The format is:
// ID Tailnet Account
static const QRegularExpression regex(R"((\w{4})\s+(\S+)\s+(\S+))");
TailAccountInfo retVal;
QRegularExpressionMatch match = regex.match(rawLineData);
if (match.hasMatch()) {
retVal.id = match.captured(1);
retVal.tailnet = match.captured(2);
retVal.account = match.captured(3);
}

return retVal;
}
};

class TailDeviceInfo : public QObject
{
Q_OBJECT
Expand Down

0 comments on commit 86e7c22

Please sign in to comment.