Kstars

ekosliveclient.cpp
1/*
2 SPDX-FileCopyrightText: 2018 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "Options.h"
8
9#include "ekosliveclient.h"
10#include "ekos/manager.h"
11
12#include "kspaths.h"
13#include "Options.h"
14#include "ekos_debug.h"
15#include "QProgressIndicator.h"
16
17#include <config-kstars.h>
18
19#ifdef HAVE_KEYCHAIN
20#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
21#include <qt6keychain/keychain.h>
22#else
23#include <qt5keychain/keychain.h>
24#endif
25#endif
26
27#include <QJsonDocument>
28#include <QJsonObject>
29#include <QNetworkAccessManager>
30#include <QNetworkReply>
31#include <QFormLayout>
32
33namespace EkosLive
34{
35Client::Client(Ekos::Manager *manager) : QDialog(manager), m_Manager(manager)
36{
37 setupUi(this);
38
39 connect(closeB, &QPushButton::clicked, this, &Client::close);
40
41 QPixmap im;
42 if (im.load(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "ekoslive.png")))
43 leftBanner->setPixmap(im);
44
45 pi = new QProgressIndicator(this);
46 bottomLayout->insertWidget(1, pi);
47
48 connectionState->setPixmap(QIcon::fromTheme("state-offline").pixmap(QSize(64, 64)));
49
50 username->setText(Options::ekosLiveUsername());
51 connect(username, &QLineEdit::editingFinished, this, [this]()
52 {
53 Options::setEkosLiveUsername(username->text());
54 });
55
56 // Initialize node managers
57 QSharedPointer<NodeManager> onlineManager(new NodeManager(NodeManager::Message | NodeManager::Media | NodeManager::Cloud));
58 connect(onlineManager.get(), &NodeManager::authenticationError, this, [this](const QString & message)
59 {
60 onlineLabel->setToolTip(message);
61 });
62
63 QSharedPointer<NodeManager> offlineManager(new NodeManager(NodeManager::Message | NodeManager::Media));
64
65 connect(offlineManager.get(), &NodeManager::authenticationError, this, [this](const QString & message)
66 {
67 offlineLabel->setToolTip(message);
68 });
69
70 m_NodeManagers.append(std::move(onlineManager));
71 m_NodeManagers.append(std::move(offlineManager));
72 syncURLs();
73
74 connect(selectServersB, &QPushButton::clicked, this, &Client::showSelectServersDialog);
75 connect(connectB, &QPushButton::clicked, this, [this]()
76 {
77 if (m_isConnected)
78 {
79 for (auto &oneManager : m_NodeManagers)
80 oneManager->disconnectNodes();
81 }
82 else
83 {
84 for (auto &oneManager : m_NodeManagers)
85 {
86 oneManager->setCredentials(username->text(), password->text());
87 oneManager->authenticate();
88 }
89 }
90 });
91
92 connect(password, &QLineEdit::returnPressed, this, [this]()
93 {
94 if (!m_isConnected)
95 {
96 for (auto &oneManager : m_NodeManagers)
97 {
98 oneManager->setCredentials(username->text(), password->text());
99 oneManager->authenticate();
100 }
101 }
102 });
103
104 rememberCredentialsCheck->setChecked(Options::rememberCredentials());
105 connect(rememberCredentialsCheck, &QCheckBox::toggled, [ = ](bool toggled)
106 {
107 Options::setRememberCredentials(toggled);
108 });
109 autoStartCheck->setChecked(Options::autoStartEkosLive());
110 connect(autoStartCheck, &QCheckBox::toggled, [ = ](bool toggled)
111 {
112 Options::setAutoStartEkosLive(toggled);
113 });
114
115#ifdef HAVE_KEYCHAIN
116 QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(QLatin1String("kstars"));
117 job->setAutoDelete(false);
118 job->setKey(QLatin1String("ekoslive"));
119 connect(job, &QKeychain::Job::finished, this, [&](QKeychain::Job * job)
120 {
121 if (job->error() == false)
122 {
123 const auto passwordText = dynamic_cast<QKeychain::ReadPasswordJob*>(job)->textData().toLatin1();
124
125 // Only set and attempt connection if the data is not empty
126 if (passwordText.isEmpty() == false && username->text().isEmpty() == false)
127 {
128 password->setText(passwordText);
129 if (autoStartCheck->isChecked())
130 {
131 for (auto &oneManager : m_NodeManagers)
132 {
133 oneManager->setCredentials(username->text(), password->text());
134 oneManager->authenticate();
135 }
136 }
137 }
138 }
139 else
140 {
141 if (autoStartCheck->isChecked())
142 qCWarning(KSTARS_EKOS) << "Failed to automatically connect due to missing EkosLive credentials:" << job->errorString();
143 }
144 job->deleteLater();
145 });
146 job->start();
147#endif
148
149 m_Message = new Message(m_Manager, m_NodeManagers);
150 connect(m_Message, &Message::connected, this, &Client::onConnected);
151 connect(m_Message, &Message::disconnected, this, &Client::onDisconnected);
152 connect(m_Message, &Message::expired, this, [&](const QUrl & url)
153 {
154 // If token expired, disconnect and reconnect again.
155 for (auto &oneManager : m_NodeManagers)
156 {
157 if (oneManager->property("websocketURL").toUrl() == url)
158 {
159 oneManager->disconnectNodes();
160 oneManager->setCredentials(username->text(), password->text());
161 oneManager->authenticate();
162 }
163 }
164 });
165
166 m_Media = new Media(m_Manager, m_NodeManagers);
167 connect(m_Media, &Media::connected, this, &Client::onConnected);
168 m_Cloud = new Cloud(m_Manager, m_NodeManagers);
169 connect(m_Cloud, &Cloud::connected, this, &Client::onConnected);
170}
171
172Client::~Client()
173{
174 for (auto &oneManager : m_NodeManagers)
175 oneManager->disconnectNodes();
176}
177
178void Client::onConnected()
179{
180 pi->stopAnimation();
181
182 m_isConnected = true;
183
184 connectB->setText(i18n("Disconnect"));
185 connectionState->setPixmap(QIcon::fromTheme("state-ok").pixmap(QSize(64, 64)));
186
187 auto disconnected = QIcon(":/icons/AlignFailure.svg").pixmap(QSize(32, 32));
188 auto connected = QIcon(":/icons/AlignSuccess.svg").pixmap(QSize(32, 32));
189
190 onlineLabel->setStyleSheet(m_NodeManagers[0]->isConnected() ? "color:white" : "color:gray");
191 onlineIcon->setPixmap(m_NodeManagers[0]->isConnected() ? connected : disconnected);
192 if (m_NodeManagers[0]->isConnected())
193 onlineLabel->setToolTip(QString());
194
195 selectServersB->setEnabled(false);
196 offlineLabel->setStyleSheet(m_NodeManagers[1]->isConnected() ? "color:white" : "color:gray");
197 offlineIcon->setPixmap(m_NodeManagers[1]->isConnected() ? connected : disconnected);
198 if (m_NodeManagers[1]->isConnected())
199 offlineLabel->setToolTip(QString());
200
201 if (rememberCredentialsCheck->isChecked())
202 {
203#ifdef HAVE_KEYCHAIN
204 QKeychain::WritePasswordJob *job = new QKeychain::WritePasswordJob(QLatin1String("kstars"));
205 job->setAutoDelete(true);
206 job->setKey(QLatin1String("ekoslive"));
207 job->setTextData(password->text());
208 job->start();
209#endif
210 }
211}
212
213void Client::onDisconnected()
214{
215 connectionState->setPixmap(QIcon::fromTheme("state-offline").pixmap(QSize(64, 64)));
216 m_isConnected = false;
217 connectB->setText(i18n("Connect"));
218
219 auto disconnected = QIcon(":/icons/AlignFailure.svg").pixmap(QSize(32, 32));
220 auto connected = QIcon(":/icons/AlignSuccess.svg").pixmap(QSize(32, 32));
221
222 onlineLabel->setStyleSheet(m_NodeManagers[0]->isConnected() ? "color:white" : "color:gray");
223 onlineIcon->setPixmap(m_NodeManagers[0]->isConnected() ? connected : disconnected);
224
225 offlineLabel->setStyleSheet(m_NodeManagers[1]->isConnected() ? "color:white" : "color:gray");
226 offlineIcon->setPixmap(m_NodeManagers[1]->isConnected() ? connected : disconnected);
227
228 selectServersB->setEnabled(true);
229}
230
231void Client::setConnected(bool enabled)
232{
233 // Return if there is no change.
234 if (enabled == m_isConnected)
235 return;
236
237 connectB->click();
238}
239
240void Client::setConfig(bool rememberCredentials, bool autoConnect)
241{
242 rememberCredentialsCheck->setChecked(rememberCredentials);
243 autoStartCheck->setChecked(autoConnect);
244}
245
246void Client::setUser(const QString &user, const QString &pass)
247{
248 username->setText(user);
249 Options::setEkosLiveUsername(user);
250
251 password->setText(pass);
252}
253
254void Client::showSelectServersDialog()
255{
256 QDialog dialog(this);
257 dialog.setMinimumWidth(300);
258 dialog.setWindowTitle(i18nc("@title:window", "Select EkosLive Servers"));
259
260 QLabel observatory(i18n("Observatory:"));
261 QLabel offline(i18n("Offline:"));
262 QLabel online(i18n("Online:"));
263 QLabel id(i18n("Machine ID:"));
264
265 QLineEdit observatoryEdit(&dialog);
266 QLineEdit offlineEdit(&dialog);
267 QLineEdit onlineEdit(&dialog);
268 QLineEdit machineID(&dialog);
269 machineID.setReadOnly(true);
270 machineID.setText(QString::fromUtf8(QSysInfo::machineUniqueId()));
271
272 observatoryEdit.setText(Options::ekosLiveObservatory());
273 offlineEdit.setText(Options::ekosLiveOfflineServer());
274 onlineEdit.setText(Options::ekosLiveOnlineServer());
275
276 QFormLayout * layout = new QFormLayout;
277 layout->addRow(&observatory, &observatoryEdit);
278 layout->addRow(&offline, &offlineEdit);
279 layout->addRow(&online, &onlineEdit);
280 layout->addRow(&id, &machineID);
281 dialog.setLayout(layout);
282 dialog.exec();
283
284 Options::setEkosLiveObservatory(observatoryEdit.text());
285 Options::setEkosLiveOfflineServer(offlineEdit.text());
286 Options::setEkosLiveOnlineServer(onlineEdit.text());
287
288 syncURLs();
289}
290
291void Client::syncURLs()
292{
293 auto onlineSSL = QUrl(Options::ekosLiveOnlineServer()).scheme() == "https";
294 auto onlineURL = QUrl(Options::ekosLiveOnlineServer()).url(QUrl::RemoveScheme);
295 if (onlineSSL)
296 m_NodeManagers[Online]->setURLs(QUrl("https:" + onlineURL), QUrl("wss:" + onlineURL));
297 else
298 m_NodeManagers[Online]->setURLs(QUrl("http:" + onlineURL), QUrl("ws:" + onlineURL));
299
300 auto offlineSSL = QUrl(Options::ekosLiveOfflineServer()).scheme() == "https";
301 auto offlineURL = QUrl(Options::ekosLiveOfflineServer()).url(QUrl::RemoveScheme);
302 if (offlineSSL)
303 m_NodeManagers[Offline]->setURLs(QUrl("https:" + offlineURL), QUrl("wss:" + offlineURL));
304 else
305 m_NodeManagers[Offline]->setURLs(QUrl("http:" + offlineURL), QUrl("ws:" + offlineURL));
306}
307}
The QProgressIndicator class lets an application display a progress indicator to show that a long tas...
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Generic record interfaces and implementations.
Definition cloud.cpp:22
void clicked(bool checked)
void toggled(bool checked)
void addRow(QLayout *layout)
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
void editingFinished()
void returnPressed()
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
QString fromUtf8(QByteArrayView str)
QByteArray machineUniqueId()
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
RemoveScheme
QString scheme() const const
QString url(FormattingOptions options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:15:11 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.