Printer webview tab with apikey injecting (username w password does not work).

Webview developer tools added but hidden in tech.
Fixed adding / removing / loading webview tabs and blinking on add.
Rename tabs and related functions.

Apikey injection from https://github.com/SoftFever/OrcaSlicer. Thank you.

Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
David Kocik 2024-02-05 13:32:27 +01:00
parent 7c1f8f8807
commit 218a24c7cf
8 changed files with 239 additions and 108 deletions

View File

@ -3689,26 +3689,34 @@ void GUI_App::handle_web_request(std::string cmd)
this->plater()->get_notification_manager()->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, out);
}
void GUI_App::show_monitor_tab(bool show, const std::string& address/* = {}*/)
void GUI_App::show_printer_webview_tab(bool show, const DynamicPrintConfig& dpc/* = {}*/)
{
std::string url = address;
if(url.find("http://") != 0 && url.find("https://") != 0) {
url = "https://" + url;
}
#if 0
if (!show) {
this->mainframe->select_tab(size_t(0));
mainframe->remove_monitor_tab();
mainframe->remove_printer_webview_tab();
} else {
std::string url = dpc.opt_string("print_host");
if (url.find("http://") != 0 && url.find("https://") != 0) {
url = "http://" + url;
}
// set password / api key
if (dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(dpc.option("printhost_authorization_type"))->value == AuthorizationType::atKeyPassword) {
mainframe->set_printer_webview_api_key(dpc.opt_string("printhost_apikey"));
}
#if 0 // The user password authentication is not working in prusa link as of now.
else {
mainframe->set_printer_webview_credentials(dpc.opt_string("printhost_user"), dpc.opt_string("printhost_password"));
}
#endif // 0
// add printer or change url
if (mainframe->get_printer_webview_tab_added()) {
mainframe->set_printer_webview_tab_url(from_u8(url));
} else {
mainframe->add_printer_webview_tab(from_u8(url));
}
}
else {
if (mainframe->get_monitor_tab_added())
mainframe->set_monitor_tab_url(boost::nowide::widen(url));
else
mainframe->add_monitor_tab(boost::nowide::widen(url));
this->mainframe->select_tab(size_t(5));
this->mainframe->select_tab(size_t(0));
}
#endif
}
} // GUI
} //Slic3r

View File

@ -411,7 +411,7 @@ public:
void request_user_logout() {}
int request_user_unbind(std::string dev_id) { return 0; }
void handle_web_request(std::string cmd);
void show_monitor_tab(bool show, const std::string& address = {});
void show_printer_webview_tab(bool show, const DynamicPrintConfig& dpc = {});
// return true if preset vas invisible and we have to installed it to make it selectable
bool select_printer_from_connect(const Preset* printer_preset);
void handle_script_message(std::string msg) {}

View File

@ -843,65 +843,74 @@ void MainFrame::create_preset_tabs()
add_created_tab(new TabPrinter(m_tabpanel), wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF ? "printer" : "sla_printer");
m_connect_webview = new ConnectWebViewPanel(m_tabpanel);
m_monitor_webview = new WebViewPanel(m_tabpanel, L"");
m_printer_webview = new PrinterWebViewPanel(m_tabpanel, L"");
// new created tabs have to be hidden by default
m_connect_webview->Hide();
m_monitor_webview->Hide();
m_printer_webview->Hide();
/*
m_media = new MediaMainPanel(this);
dynamic_cast<TopBar*>(m_tabpanel)->AddPage(m_media, "Media");
*/
}
void MainFrame::enable_connect_tab()
void MainFrame::add_connect_webview_tab()
{
if (m_connect_webview_added)
return;
size_t selected = m_tabpanel->GetSelection();
dynamic_cast<TopBar*>(m_tabpanel)->AddPage(m_connect_webview, "PrusaConnect", "", true);
m_connect_webview->load_default_url();
m_tabpanel->SetSelection(selected);
assert(!m_connect_webview_added);
// parameters of InsertPage (to prevent ambigous overloaded function)
// insert to positon 4, if physical printer is already added, it moves to 5
// order of tabs: Plater - Print Settings - Filaments - Printers - Prusa Connect - Prusa Link
size_t n = 4;
wxWindow* page = m_connect_webview;
const wxString text(L"Prusa Connect");
const std::string bmp_name = "";
bool bSelect = false;
dynamic_cast<TopBar*>(m_tabpanel)->InsertPage(n, page, text, bmp_name, bSelect);
m_connect_webview->load_default_url_delayed();
m_connect_webview_added = true;
}
void MainFrame::disable_connect_tab()
void MainFrame::remove_connect_webview_tab()
{
if (!m_connect_webview_added)
return;
size_t selected = m_tabpanel->GetSelection();
if (selected == 4)
assert(m_connect_webview_added);
// connect tab should always be at position 4
if (m_tabpanel->GetSelection() == 4)
m_tabpanel->SetSelection(0);
dynamic_cast<TopBar*>(m_tabpanel)->RemovePage(4);
m_connect_webview_added = false;
}
void MainFrame::add_monitor_tab(const wxString& url)
void MainFrame::add_printer_webview_tab(const wxString& url)
{
if (m_monitor_webview_added)
return;
size_t selected = m_tabpanel->GetSelection();
dynamic_cast<TopBar*>(m_tabpanel)->AddPage(m_monitor_webview, "Monitor", "", true);
m_monitor_webview->set_default_url(url);
m_monitor_webview->load_default_url();
m_tabpanel->SetSelection(selected);
m_monitor_webview_added = true;
assert(!m_printer_webview_added);
m_printer_webview_added = true;
// add as the last (rightmost) panel
dynamic_cast<TopBar*>(m_tabpanel)->AddPage(m_printer_webview, L"Physical Printer", "", false);
m_printer_webview->set_default_url(url);
m_printer_webview->load_default_url_delayed();
}
void MainFrame::remove_monitor_tab()
void MainFrame::remove_printer_webview_tab()
{
if (!m_monitor_webview_added)
return;
size_t selected = m_tabpanel->GetSelection();
if (selected == 4)
m_tabpanel->SetSelection(0);
assert(m_printer_webview_added);
m_printer_webview_added = false;
m_printer_webview->Hide();
// always remove the last tab
dynamic_cast<TopBar*>(m_tabpanel)->RemovePage(m_tabpanel->GetPageCount() - 1);
m_monitor_webview_added = false;
}
void MainFrame::set_monitor_tab_url(const wxString& url)
void MainFrame::set_printer_webview_tab_url(const wxString& url)
{
assert(m_monitor_webview_added);
m_monitor_webview->set_default_url(url);
m_monitor_webview->load_default_url();
assert(m_printer_webview_added);
m_printer_webview->clear();
m_printer_webview->set_default_url(url);
if (m_tabpanel->GetSelection() == m_tabpanel->GetPageCount() - 1) {
m_printer_webview->load_url(url);
} else {
m_printer_webview->load_default_url_delayed();
}
}
void MainFrame::set_printer_webview_api_key(const std::string& key)
{
m_printer_webview->set_api_key(key);
}
void MainFrame::set_printer_webview_credentials(const std::string& usr, const std::string& psk)
{
m_printer_webview->set_credentials(usr, psk);
}
void Slic3r::GUI::MainFrame::refresh_account_menu(bool avatar/* = false */)

View File

@ -44,7 +44,7 @@ class MainFrame;
class PreferencesDialog;
class GalleryDialog;
class WebViewPanel;
class MediaMainPanel;
class PrinterWebViewPanel;
enum QuickSlice
{
@ -98,10 +98,10 @@ class MainFrame : public DPIFrame
size_t m_last_selected_tab;
Search::OptionsSearcher m_searcher;
WebViewPanel* m_connect_webview{ nullptr };
bool m_connect_webview_added{ false };
WebViewPanel* m_monitor_webview{ nullptr };
bool m_monitor_webview_added{ false };
WebViewPanel* m_connect_webview{ nullptr };
bool m_connect_webview_added{ false };
PrinterWebViewPanel* m_printer_webview{ nullptr };
bool m_printer_webview_added{ false };
std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const;
std::string get_dir_name(const wxString &full_name) const;
@ -215,13 +215,15 @@ public:
void add_to_recent_projects(const wxString& filename);
void technology_changed();
void enable_connect_tab();
void disable_connect_tab();
void add_connect_webview_tab();
void remove_connect_webview_tab();
void add_monitor_tab(const wxString& url);
void remove_monitor_tab();
void set_monitor_tab_url(const wxString& url);
bool get_monitor_tab_added() const { return m_monitor_webview_added; }
void add_printer_webview_tab(const wxString& url);
void remove_printer_webview_tab();
void set_printer_webview_tab_url(const wxString& url);
bool get_printer_webview_tab_added() const { return m_printer_webview_added; }
void set_printer_webview_api_key(const std::string& key);
void set_printer_webview_credentials(const std::string& usr, const std::string& psk);
void refresh_account_menu(bool avatar = false);

View File

@ -888,8 +888,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
std::string text = _u8L("Logged out from Prusa Account.");
this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
this->main_frame->disable_connect_tab();
this->main_frame->remove_connect_webview_tab();
this->main_frame->refresh_account_menu(true);
// Update sidebar printer status
sidebar->update_printer_presets_combobox();
});
this->q->Bind(EVT_UA_ID_USER_SUCCESS, [this](UserAccountSuccessEvent& evt) {
@ -900,7 +902,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
// show connect tab
this->main_frame->enable_connect_tab();
this->main_frame->add_connect_webview_tab();
// Update User name in TopBar
this->main_frame->refresh_account_menu();
} else {
@ -910,6 +912,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
user_account->clear();
this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Failed to connect to Prusa Account."));
this->main_frame->remove_connect_webview_tab();
// Update User name in TopBar
this->main_frame->refresh_account_menu();
// Update sidebar printer status
@ -922,6 +925,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
user_account->clear();
this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Failed to connect to Prusa Account."));
this->main_frame->remove_connect_webview_tab();
// Update User name in TopBar
this->main_frame->refresh_account_menu();
// Update sidebar printer status

View File

@ -718,12 +718,11 @@ void Sidebar::on_select_preset(wxCommandEvent& evt)
*/
m_object_list->update_object_list_by_printer_technology();
if (combo->is_selected_physical_printer())
{
wxGetApp().show_monitor_tab(true, wxGetApp().preset_bundle->physical_printers.get_selected_printer().config.opt_string("print_host"));
if (combo->is_selected_physical_printer()) {
wxGetApp().show_printer_webview_tab(true, wxGetApp().preset_bundle->physical_printers.get_selected_printer().config);
} else {
wxGetApp().show_printer_webview_tab(false);
}
else
wxGetApp().show_monitor_tab(false);
}
#ifdef __WXMSW__

View File

@ -95,7 +95,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url)
wxMenuItem* viewSource = m_tools_menu->Append(wxID_ANY, _L("View Source"));
wxMenuItem* viewText = m_tools_menu->Append(wxID_ANY, _L("View Text"));
m_tools_menu->AppendSeparator();
wxMenu* script_menu = new wxMenu;
m_script_custom = script_menu->Append(wxID_ANY, "Custom script");
@ -103,6 +103,9 @@ WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url)
wxMenuItem* addUserScript = m_tools_menu->Append(wxID_ANY, _L("Add user script"));
wxMenuItem* setCustomUserAgent = m_tools_menu->Append(wxID_ANY, _L("Set custom user agent"));
m_context_menu = m_tools_menu->AppendCheckItem(wxID_ANY, _L("Enable Context Menu"));
m_dev_tools = m_tools_menu->AppendCheckItem(wxID_ANY, _L("Enable Dev Tools"));
#endif
//Zoom
m_zoomFactor = 100;
@ -116,16 +119,18 @@ WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url)
#ifdef DEBUG_URL_PANEL
// Connect the button events
Bind(wxEVT_BUTTON, &WebViewPanel::on_back, this, m_button_back->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_forward, this, m_button_forward->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_stop, this, m_button_stop->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_reload, this, m_button_reload->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_back_button, this, m_button_back->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_forward_button, this, m_button_forward->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_stop_button, this, m_button_stop->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_reload_button, this, m_button_reload->GetId());
Bind(wxEVT_BUTTON, &WebViewPanel::on_tools_clicked, this, m_button_tools->GetId());
Bind(wxEVT_TEXT_ENTER, &WebViewPanel::on_url, this, m_url->GetId());
// Connect the menu events
Bind(wxEVT_MENU, &WebViewPanel::on_view_source_request, this, viewSource->GetId());
Bind(wxEVT_MENU, &WebViewPanel::on_view_text_request, this, viewText->GetId());
Bind(wxEVT_MENU, &WebViewPanel::On_enable_context_menu, this, m_context_menu->GetId());
Bind(wxEVT_MENU, &WebViewPanel::On_enable_dev_tools, this, m_dev_tools->GetId());
Bind(wxEVT_MENU, &WebViewPanel::on_run_script_custom, this, m_script_custom->GetId());
Bind(wxEVT_MENU, &WebViewPanel::on_add_user_script, this, addUserScript->GetId());
@ -152,7 +157,7 @@ WebViewPanel::~WebViewPanel()
}
void WebViewPanel::load_url(wxString& url)
void WebViewPanel::load_url(const wxString& url)
{
this->Show();
this->Raise();
@ -163,15 +168,20 @@ void WebViewPanel::load_url(wxString& url)
m_browser->SetFocus();
}
void WebViewPanel::load_default_url()
void WebViewPanel::load_default_url_delayed()
{
assert(!m_default_url.empty());
load_url(m_default_url);
m_load_default_url = true;
}
void WebViewPanel::on_show(wxShowEvent& evt)
{
if (evt.IsShown() && m_load_default_url)
{
m_load_default_url = false;
load_url(m_default_url);
}
// TODO: add check that any url was loaded
}
void WebViewPanel::on_idle(wxIdleEvent& WXUNUSED(evt))
@ -200,7 +210,7 @@ void WebViewPanel::on_url(wxCommandEvent& WXUNUSED(evt))
/**
* Callback invoked when user pressed the "back" button
*/
void WebViewPanel::on_back(wxCommandEvent& WXUNUSED(evt))
void WebViewPanel::on_back_button(wxCommandEvent& WXUNUSED(evt))
{
m_browser->GoBack();
}
@ -208,7 +218,7 @@ void WebViewPanel::on_back(wxCommandEvent& WXUNUSED(evt))
/**
* Callback invoked when user pressed the "forward" button
*/
void WebViewPanel::on_forward(wxCommandEvent& WXUNUSED(evt))
void WebViewPanel::on_forward_button(wxCommandEvent& WXUNUSED(evt))
{
m_browser->GoForward();
}
@ -216,7 +226,7 @@ void WebViewPanel::on_forward(wxCommandEvent& WXUNUSED(evt))
/**
* Callback invoked when user pressed the "stop" button
*/
void WebViewPanel::on_stop(wxCommandEvent& WXUNUSED(evt))
void WebViewPanel::on_stop_button(wxCommandEvent& WXUNUSED(evt))
{
m_browser->Stop();
}
@ -224,7 +234,7 @@ void WebViewPanel::on_stop(wxCommandEvent& WXUNUSED(evt))
/**
* Callback invoked when user pressed the "reload" button
*/
void WebViewPanel::on_reload(wxCommandEvent& WXUNUSED(evt))
void WebViewPanel::on_reload_button(wxCommandEvent& WXUNUSED(evt))
{
m_browser->Reload();
}
@ -279,6 +289,9 @@ void WebViewPanel::on_view_text_request(wxCommandEvent& WXUNUSED(evt))
void WebViewPanel::on_tools_clicked(wxCommandEvent& WXUNUSED(evt))
{
#ifdef DEBUG_URL_PANEL
m_context_menu->Check(m_browser->IsContextMenuEnabled());
m_dev_tools->Check(m_browser->IsAccessToDevToolsEnabled());
wxPoint position = ScreenToClient(wxGetMousePosition());
PopupMenu(m_tools_menu, position.x, position.y);
#endif
@ -364,6 +377,15 @@ void WebViewPanel::on_select_all(wxCommandEvent& WXUNUSED(evt))
m_browser->SelectAll();
}
void WebViewPanel::On_enable_context_menu(wxCommandEvent& evt)
{
m_browser->EnableContextMenu(evt.IsChecked());
}
void WebViewPanel::On_enable_dev_tools(wxCommandEvent& evt)
{
m_browser->EnableAccessToDevTools(evt.IsChecked());
}
/**
* Callback invoked when a loading error occurs
*/
@ -387,6 +409,7 @@ case type: \
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_OTHER);
}
BOOST_LOG_TRIVIAL(warning) << "WebView error: " << category;
//Show the info bar with an error
#ifdef DEBUG_URL_PANEL
@ -418,18 +441,6 @@ ConnectWebViewPanel::ConnectWebViewPanel(wxWindow* parent)
m_actions["requestAccessToken"] = std::bind(&ConnectWebViewPanel::connect_set_access_token, this);
m_actions["requestLanguage"] = std::bind(&ConnectWebViewPanel::connect_set_language, this);
}
void ConnectWebViewPanel::on_show(wxShowEvent& evt)
{
// run script with access token to login
/*
if (evt.IsShown()) {
std::string token = wxGetApp().plater()->get_user_account()->get_access_token();
wxString script = GUI::format_wxstr("window._prusaConnect.setAccessToken(\'%1%\')", token);
// TODO: should this be happening every OnShow?
run_script(script);
}
*/
}
void ConnectWebViewPanel::on_script_message(wxWebViewEvent& evt)
{
@ -490,6 +501,74 @@ void ConnectWebViewPanel::connect_set_language()
run_script(script);
}
PrinterWebViewPanel::PrinterWebViewPanel(wxWindow* parent, const wxString& default_url)
: WebViewPanel(parent, default_url)
{
m_browser->Bind(wxEVT_WEBVIEW_LOADED, &PrinterWebViewPanel::on_loaded, this);
}
void PrinterWebViewPanel::on_loaded(wxWebViewEvent& evt)
{
if (evt.GetURL().IsEmpty())
return;
if (!m_api_key.empty()) {
send_api_key();
} else if (!m_usr.empty() && !m_psk.empty()) {
send_credentials();
}
}
void PrinterWebViewPanel::send_api_key()
{
if (m_api_key_sent)
return;
m_api_key_sent = true;
wxString key = from_u8(m_api_key);
wxString script = wxString::Format(R"(
// Check if window.fetch exists before overriding
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function(input, init = {}) {
init.headers = init.headers || {};
init.headers['X-API-Key'] = '%s';
return originalFetch(input, init);
};
}
)",
key);
m_browser->RemoveAllUserScripts();
m_browser->AddUserScript(script);
m_browser->Reload();
}
void PrinterWebViewPanel::send_credentials()
{
if (m_api_key_sent)
return;
m_api_key_sent = true;
wxString usr = from_u8(m_usr);
wxString psk = from_u8(m_psk);
wxString script = wxString::Format(R"(
// Check if window.fetch exists before overriding
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function(input, init = {}) {
init.headers = init.headers || {};
init.headers['X-API-Key'] = 'Basic ' + btoa(`%s:%s`);
return originalFetch(input, init);
};
}
)", usr, psk);
m_browser->RemoveAllUserScripts();
m_browser->AddUserScript(script);
m_browser->Reload();
}
WebViewDialog::WebViewDialog(wxWindow* parent, const wxString& url)
: wxDialog(parent, wxID_ANY, "Webview Dialog", wxDefaultPosition, wxSize(1366, 768)/* wxSize(100 * wxGetApp().em_unit(), 100 * wxGetApp().em_unit())*/)
{

View File

@ -30,24 +30,25 @@ namespace Slic3r {
namespace GUI {
//#define DEBUG_URL_PANEL
class WebViewPanel : public wxPanel
{
public:
WebViewPanel(wxWindow *parent, const wxString& default_url);
virtual ~WebViewPanel();
void load_url(wxString& url);
void load_default_url();
void load_url(const wxString& url);
void load_default_url_delayed();
virtual void on_show(wxShowEvent& evt);
void on_show(wxShowEvent& evt);
virtual void on_script_message(wxWebViewEvent& evt);
void on_idle(wxIdleEvent& evt);
void on_url(wxCommandEvent& evt);
void on_back(wxCommandEvent& evt);
void on_forward(wxCommandEvent& evt);
void on_stop(wxCommandEvent& evt);
void on_reload(wxCommandEvent& evt);
void on_back_button(wxCommandEvent& evt);
void on_forward_button(wxCommandEvent& evt);
void on_stop_button(wxCommandEvent& evt);
void on_reload_button(wxCommandEvent& evt);
void on_view_source_request(wxCommandEvent& evt);
void on_view_text_request(wxCommandEvent& evt);
@ -61,16 +62,24 @@ public:
void on_clear_selection(wxCommandEvent& evt);
void on_delete_selection(wxCommandEvent& evt);
void on_select_all(wxCommandEvent& evt);
void On_enable_context_menu(wxCommandEvent& evt);
void On_enable_dev_tools(wxCommandEvent& evt);
void on_close(wxCloseEvent& evt);
wxTimer * m_LoginUpdateTimer{nullptr};
wxString get_default_url() const { return m_default_url; }
void set_default_url(const wxString& url) { m_default_url = url; }
virtual bool Show(bool show = true) override
{
return wxPanel::Show(show);
}
protected:
wxWebView* m_browser;
bool m_load_default_url { false };
#ifdef DEBUG_URL_PANEL
wxBoxSizer *bSizer_toolbar;
@ -86,6 +95,9 @@ protected:
wxInfoBar *m_info;
wxStaticText* m_info_text;
wxMenuItem* m_context_menu;
wxMenuItem* m_dev_tools;
#endif
long m_zoomFactor;
@ -101,7 +113,6 @@ class ConnectWebViewPanel : public WebViewPanel
{
public:
ConnectWebViewPanel(wxWindow* parent);
void on_show(wxShowEvent& evt) override;
void on_script_message(wxWebViewEvent& evt) override;
void connect_set_access_token();
@ -110,6 +121,25 @@ private:
std::map<std::string, std::function<void(void)>> m_actions;
};
class PrinterWebViewPanel : public WebViewPanel
{
public:
PrinterWebViewPanel(wxWindow* parent, const wxString& default_url);
void on_loaded(wxWebViewEvent& evt);
void send_api_key();
void send_credentials();
void set_api_key(const std::string& key) { m_api_key = key; }
void set_credentials(const std::string& usr, const std::string& psk) { m_usr = usr; m_psk = psk; }
void clear() { m_api_key.clear(); m_usr.clear(); m_psk.clear(); m_api_key_sent = false; }
private:
std::string m_api_key;
std::string m_usr;
std::string m_psk;
bool m_api_key_sent {false};
};
class WebViewDialog : public wxDialog
{