Skip to content

Commit a10796e

Browse files
authored
Merge pull request #931 from deXol/developBleTutorial
[BLE] Add tutorial for Moolticute
2 parents c3db57f + b3b0490 commit a10796e

6 files changed

Lines changed: 348 additions & 2 deletions

File tree

gui.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ SOURCES += src/main_gui.cpp \
2929
src/ParseDomain.cpp \
3030
src/Common.cpp \
3131
src/TOTPCredential.cpp \
32+
src/TutorialWidget.cpp \
3233
src/WSClient.cpp \
3334
src/RotateSpinner.cpp \
3435
src/AppGui.cpp \
@@ -82,6 +83,7 @@ HEADERS += src/MainWindow.h \
8283
src/Common.h \
8384
src/QtHelper.h \
8485
src/TOTPCredential.h \
86+
src/TutorialWidget.h \
8587
src/WSClient.h \
8688
src/RotateSpinner.h \
8789
src/utils/GridLayoutUtil.h \

src/MainWindow.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,8 +1149,11 @@ void MainWindow::handleBackupImported()
11491149

11501150
void MainWindow::showPrompt(PromptMessage * message)
11511151
{
1152-
ui->promptWidget->setPromptMessage(message);
1153-
ui->promptWidget->show();
1152+
if (ui->tutorialWidget->isTutorialFinished())
1153+
{
1154+
ui->promptWidget->setPromptMessage(message);
1155+
ui->promptWidget->show();
1156+
}
11541157
}
11551158

11561159
void MainWindow::hidePrompt()
@@ -1608,6 +1611,13 @@ void MainWindow::updateTabButtons()
16081611
}
16091612
};
16101613

1614+
if (!ui->tutorialWidget->isTutorialFinished())
1615+
{
1616+
setEnabledToAllTabButtons(false);
1617+
ui->tutorialWidget->displayCurrentTab();
1618+
return;
1619+
}
1620+
16111621
if (ui->stackedWidget->currentWidget() == ui->pageWaiting)
16121622
{
16131623
setEnabledToAllTabButtons(false);
@@ -2264,3 +2274,8 @@ void MainWindow::on_checkBoxBackupNotification_stateChanged(int)
22642274
QSettings s;
22652275
s.setValue("settings/backup_notification", isBackupNotification());
22662276
}
2277+
2278+
void MainWindow::on_checkBoxTutorial_stateChanged(int arg1)
2279+
{
2280+
ui->tutorialWidget->changeTutorialFinished(Qt::Checked == arg1);
2281+
}

src/MainWindow.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class MainWindow : public QMainWindow
7777
friend class SettingsGuiHelper;
7878
friend class SettingsGuiMini;
7979
friend class SettingsGuiBLE;
80+
friend class TutorialWidget;
8081

8182
signals:
8283
void windowCloseRequested();
@@ -194,6 +195,8 @@ private slots:
194195

195196
void on_checkBoxBackupNotification_stateChanged(int arg1);
196197

198+
void on_checkBoxTutorial_stateChanged(int arg1);
199+
197200
protected:
198201
virtual void keyPressEvent(QKeyEvent *event) override;
199202
virtual void keyReleaseEvent(QKeyEvent *event) override;

src/MainWindow.ui

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4106,6 +4106,49 @@ Hint: keep your mouse positioned over an option to get more details.</string>
41064106
</item>
41074107
</layout>
41084108
</item>
4109+
<item>
4110+
<layout class="QHBoxLayout" name="horizontalLayout_37">
4111+
<item>
4112+
<widget class="QLabel" name="label_12">
4113+
<property name="text">
4114+
<string>Display Moolticute Tutorial</string>
4115+
</property>
4116+
</widget>
4117+
</item>
4118+
<item>
4119+
<widget class="QLabel" name="labelTutorialRestart">
4120+
<property name="font">
4121+
<font>
4122+
<italic>true</italic>
4123+
</font>
4124+
</property>
4125+
<property name="text">
4126+
<string>(Restart Needed)</string>
4127+
</property>
4128+
</widget>
4129+
</item>
4130+
<item>
4131+
<spacer name="horizontalSpacer_36">
4132+
<property name="orientation">
4133+
<enum>Qt::Horizontal</enum>
4134+
</property>
4135+
<property name="sizeHint" stdset="0">
4136+
<size>
4137+
<width>40</width>
4138+
<height>20</height>
4139+
</size>
4140+
</property>
4141+
</spacer>
4142+
</item>
4143+
<item>
4144+
<widget class="QCheckBox" name="checkBoxTutorial">
4145+
<property name="text">
4146+
<string>Display tutorial</string>
4147+
</property>
4148+
</widget>
4149+
</item>
4150+
</layout>
4151+
</item>
41094152
<item>
41104153
<spacer name="horizontalSpacer_44">
41114154
<property name="orientation">
@@ -4753,6 +4796,16 @@ Hint: keep your mouse positioned over an option to get more details.</string>
47534796
</property>
47544797
</widget>
47554798
</item>
4799+
<item>
4800+
<widget class="TutorialWidget" name="tutorialWidget">
4801+
<property name="frameShape">
4802+
<enum>QFrame::StyledPanel</enum>
4803+
</property>
4804+
<property name="frameShadow">
4805+
<enum>QFrame::Raised</enum>
4806+
</property>
4807+
</widget>
4808+
</item>
47564809
</layout>
47574810
</widget>
47584811
</widget>
@@ -4800,6 +4853,12 @@ Hint: keep your mouse positioned over an option to get more details.</string>
48004853
<header>PromptWidget.h</header>
48014854
<container>1</container>
48024855
</customwidget>
4856+
<customwidget>
4857+
<class>TutorialWidget</class>
4858+
<extends>QFrame</extends>
4859+
<header>TutorialWidget.h</header>
4860+
<container>1</container>
4861+
</customwidget>
48034862
<customwidget>
48044863
<class>NotesManagement</class>
48054864
<extends>QWidget</extends>

src/TutorialWidget.cpp

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#include "TutorialWidget.h"
2+
3+
#include <QHBoxLayout>
4+
#include <QLabel>
5+
#include <QPushButton>
6+
7+
#include "MainWindow.h"
8+
#include "ui_MainWindow.h"
9+
10+
const QString TutorialWidget::TUTORIAL_HEADER_TEXT = tr("<b>Moolticute Tutorial - %1</b><br><br>");
11+
const QString TutorialWidget::TUTORIAL_FINISHED_SETTING = "settings/tutorial_finished";
12+
13+
TutorialWidget::TutorialWidget(QWidget *parent) :
14+
QFrame(parent),
15+
m_messageLabel{new QLabel},
16+
m_nextButton{new QPushButton},
17+
m_exitButton{new QPushButton}
18+
{
19+
QHBoxLayout *lay = new QHBoxLayout(this);
20+
setLayout(lay);
21+
22+
m_messageLabel->setMinimumWidth(600);
23+
24+
lay->addStretch();
25+
lay->addWidget(m_messageLabel);
26+
lay->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
27+
lay->addWidget(m_nextButton);
28+
lay->addWidget(m_exitButton);
29+
lay->addStretch();
30+
31+
setStyleSheet("TutorialWidget {border: 15px solid #60B1C7;}");
32+
33+
m_messageLabel->setAlignment(Qt::AlignCenter);
34+
m_messageLabel->setTextFormat(Qt::RichText);
35+
m_messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
36+
m_messageLabel->setOpenExternalLinks(true);
37+
38+
m_nextButton->setText(tr("Next"));
39+
m_nextButton->setStyleSheet(CSS_BLUE_BUTTON);
40+
41+
m_exitButton->setText(tr("Exit"));
42+
m_exitButton->setStyleSheet(CSS_BLUE_BUTTON);
43+
connect(m_exitButton, &QPushButton::clicked, this, &TutorialWidget::onExitClicked);
44+
connect(m_nextButton, &QPushButton::clicked, this, &TutorialWidget::onNextClicked);
45+
46+
QSettings s;
47+
m_tutorialFinished = s.value(TUTORIAL_FINISHED_SETTING, false).toBool();
48+
m_mw = static_cast<MainWindow*>(parent->parent());
49+
if (m_tutorialFinished)
50+
{
51+
hide();
52+
}
53+
else
54+
{
55+
initTutorial();
56+
startTutorial();
57+
show();
58+
}
59+
60+
if (!m_tutorialFinished)
61+
{
62+
connect(m_mw->wsClient, &WSClient::deviceConnected, this, &TutorialWidget::onDeviceConnected);
63+
connect(m_mw->wsClient, &WSClient::deviceDisconnected, this, &TutorialWidget::onDeviceDisconnected);
64+
connect(m_mw->wsClient, &WSClient::statusChanged, this, &TutorialWidget::onStatusChanged);
65+
}
66+
}
67+
68+
TutorialWidget::~TutorialWidget()
69+
{
70+
71+
}
72+
73+
void TutorialWidget::setText(const QString &text)
74+
{
75+
m_messageLabel->setText(text);
76+
}
77+
78+
void TutorialWidget::displayCurrentTab()
79+
{
80+
if (m_current_index != -1)
81+
{
82+
m_mw->ui->widgetHeader->setEnabled(true);
83+
m_tabs[m_current_index].button()->setEnabled(true);
84+
}
85+
}
86+
87+
void TutorialWidget::changeTutorialFinished(bool enabled)
88+
{
89+
QSettings s;
90+
s.setValue(TUTORIAL_FINISHED_SETTING, !enabled);
91+
}
92+
93+
void TutorialWidget::onExitClicked()
94+
{
95+
QSettings s;
96+
s.setValue(TUTORIAL_FINISHED_SETTING, true);
97+
m_tutorialFinished = true;
98+
hide();
99+
disconnect(m_mw->wsClient, &WSClient::deviceConnected, this, &TutorialWidget::onDeviceConnected);
100+
disconnect(m_mw->wsClient, &WSClient::deviceDisconnected, this, &TutorialWidget::onDeviceDisconnected);
101+
disconnect(m_mw->wsClient, &WSClient::statusChanged, this, &TutorialWidget::onStatusChanged);
102+
m_mw->updateTabButtons();
103+
}
104+
105+
void TutorialWidget::onNextClicked()
106+
{
107+
if (m_current_index != -1)
108+
{
109+
m_tabs[m_current_index].button()->setEnabled(false);
110+
}
111+
m_mw->ui->widgetHeader->setEnabled(true);
112+
m_current_index++;
113+
if (m_current_index >= m_tabs.size())
114+
{
115+
qCritical() << "No more tutorial page";
116+
QMessageBox::information(this, tr("Tutorial finished"), tr("Congratulation, you have completed the tutorial."));
117+
onExitClicked();
118+
return;
119+
}
120+
QPushButton *button = m_tabs[m_current_index].button();
121+
button->setEnabled(true);
122+
QWidget* page = nullptr;
123+
for (auto* tabButton : m_mw->m_tabMap)
124+
{
125+
if (tabButton == button)
126+
{
127+
page = m_mw->m_tabMap.key(button);
128+
}
129+
}
130+
if (page)
131+
{
132+
m_mw->ui->stackedWidget->setCurrentWidget(page);
133+
}
134+
else
135+
{
136+
qCritical() << "Cannot find page!";
137+
}
138+
m_messageLabel->setText(TUTORIAL_HEADER_TEXT.arg(button->text()) + m_tabs[m_current_index].text());
139+
}
140+
141+
void TutorialWidget::onDeviceConnected()
142+
{
143+
if (!m_tutorialFinished)
144+
{
145+
bool connected = m_mw->wsClient->isMPBLE();
146+
if (connected)
147+
{
148+
m_nextButton->setEnabled(Common::MPStatus::Unlocked == m_mw->wsClient->get_status());
149+
}
150+
else
151+
{
152+
m_nextButton->setEnabled(false);
153+
QMessageBox::warning(this, tr("Tutorial warning"), tr("Tutorial is only available for BLE device, please attach one or Exit tutorial."));
154+
}
155+
}
156+
}
157+
158+
void TutorialWidget::onDeviceDisconnected()
159+
{
160+
if (!m_tutorialFinished)
161+
{
162+
m_nextButton->setEnabled(false);
163+
// Display connect device again
164+
startTutorial();
165+
}
166+
}
167+
168+
void TutorialWidget::onStatusChanged(Common::MPStatus status)
169+
{
170+
if (!m_tutorialFinished)
171+
{
172+
m_nextButton->setEnabled(Common::MPStatus::Unlocked == status);
173+
}
174+
}
175+
176+
void TutorialWidget::initTutorial()
177+
{
178+
Ui::MainWindow* ui = m_mw->ui;
179+
m_tabs = {
180+
{ui->pushButtonDevSettings, tr("You can use the ‘Device Settings’ tab to change parameters on your Mooltipass device.<br>Hovering the mouse pointer over an option will gives a short description of what that option does.")},
181+
{ui->pushButtonCred, tr("In case you aren't using our browser extensions, this tab allows you to manually add credentials to your database.<br>By clicking the \"Enter Credentials Management Mode\" button, you will be able to visualize and<br>modify the credentials stored on your database.")},
182+
{ui->pushButtonNotes, tr("This tab allows you to securely store text on your device. Simply add a new note, set a title and type its contents.")},
183+
{ui->pushButtonFiles, tr("The Mooltipass device can operate as a flash drive.<br>The ‘Files’ tab allows you to add, store and access small files on your Mooltipass.")},
184+
{ui->pushButtonFido, tr("Similarly to the credentials tab, the FIDO2 tab allows you to visualize and delete FIDO2 credentials stored on your device.")},
185+
{ui->pushButtonSync, tr("The different buttons in the synchronization tab will allow you to backup and<br>restore the credentials you have stored inside the memory of your Mooltipass.<br>You can also select a backup monitoring file to make sure your Mooltipass database is always in sync with it.")},
186+
{ui->pushButtonAppSettings, tr("The ‘Settings’ tab lets you change parameters on the Moolticute app.")},
187+
{ui->pushButtonAbout, tr("Finally, the ‘About’ tab provides you with general information about your device and app.<br>It allows you to check that you are running the latest version of Moolticute.")}
188+
};
189+
}
190+
191+
void TutorialWidget::startTutorial()
192+
{
193+
m_current_index = -1;
194+
auto connected = m_mw->wsClient->isConnected();
195+
if (!connected)
196+
{
197+
m_nextButton->setEnabled(false);
198+
}
199+
m_messageLabel->setText(TUTORIAL_HEADER_TEXT.arg("Welcome") + tr("Welcome to Mooltice tutorial! Please connect your device and unlock it to continue the tutorial."));
200+
}

0 commit comments

Comments
 (0)