diff --git a/resources/web/connect_loading_reload.html b/resources/web/connect_loading_reload.html index 7a1909a5de..018926044c 100644 --- a/resources/web/connect_loading_reload.html +++ b/resources/web/connect_loading_reload.html @@ -71,7 +71,7 @@

- + diff --git a/resources/web/other_loading_reload.html b/resources/web/other_loading_reload.html index e07a212c4c..68033cdc37 100644 --- a/resources/web/other_loading_reload.html +++ b/resources/web/other_loading_reload.html @@ -62,7 +62,7 @@

- + diff --git a/src/slic3r/GUI/ConfigWizardWebViewPage.cpp b/src/slic3r/GUI/ConfigWizardWebViewPage.cpp index 7d1a915c66..075eb9539a 100644 --- a/src/slic3r/GUI/ConfigWizardWebViewPage.cpp +++ b/src/slic3r/GUI/ConfigWizardWebViewPage.cpp @@ -125,7 +125,7 @@ void ConfigWizardWebViewPage::on_navigation_request(wxWebViewEvent &evt) { wxString url = evt.GetURL(); if (url.starts_with(L"prusaslicer")) { - delete_cookies(m_browser, "https://account.prusa3d.com"); + delete_cookies(m_browser, Utils::ServiceConfig::instance().account_url()); delete_cookies(m_browser, "https://accounts.google.com"); delete_cookies(m_browser, "https://appleid.apple.com"); delete_cookies(m_browser, "https://facebook.com"); diff --git a/src/slic3r/GUI/Downloader.cpp b/src/slic3r/GUI/Downloader.cpp index 61e69227bb..5234cded6b 100644 --- a/src/slic3r/GUI/Downloader.cpp +++ b/src/slic3r/GUI/Downloader.cpp @@ -90,14 +90,14 @@ std::string unescape_url(const std::string& unescaped) } } -Download::Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder) +Download::Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder, bool load_after) : m_id(ID) , m_filename(filename_from_url(url)) , m_dest_folder(dest_folder) { assert(boost::filesystem::is_directory(dest_folder)); m_final_path = dest_folder / m_filename; - m_file_get = std::make_shared(ID, std::move(url), m_filename, evt_handler, dest_folder); + m_file_get = std::make_shared(ID, std::move(url), m_filename, evt_handler, dest_folder, load_after); } void Download::start() @@ -132,11 +132,6 @@ void Download::resume() Downloader::Downloader() : wxEvtHandler() { - //Bind(EVT_DWNLDR_FILE_COMPLETE, [](const wxCommandEvent& evt) {}); - //Bind(EVT_DWNLDR_FILE_PROGRESS, [](const wxCommandEvent& evt) {}); - //Bind(EVT_DWNLDR_FILE_ERROR, [](const wxCommandEvent& evt) {}); - //Bind(EVT_DWNLDR_FILE_NAME_CHANGE, [](const wxCommandEvent& evt) {}); - Bind(EVT_DWNLDR_FILE_COMPLETE, &Downloader::on_complete, this); Bind(EVT_DWNLDR_FILE_PROGRESS, &Downloader::on_progress, this); Bind(EVT_DWNLDR_FILE_ERROR, &Downloader::on_error, this); @@ -169,14 +164,38 @@ void Downloader::start_download(const std::string& full_url) return; } - std::string text(escaped_url); - m_downloads.emplace_back(std::make_unique(id, std::move(escaped_url), this, m_dest_folder)); + m_downloads.emplace_back(std::make_unique(id, std::move(escaped_url), this, m_dest_folder, true)); NotificationManager* ntf_mngr = wxGetApp().notification_manager(); ntf_mngr->push_download_URL_progress_notification(id, m_downloads.back()->get_filename(), std::bind(&Downloader::user_action_callback, this, std::placeholders::_1, std::placeholders::_2)); m_downloads.back()->start(); BOOST_LOG_TRIVIAL(debug) << "started download"; } +void Downloader::start_download_printables(const std::string& url, bool load_after, const std::string& printables_url, GUI_App* app) +{ + assert(m_initialized); + + size_t id = get_next_id(); + + if (!boost::starts_with(url, "https://") || !FileGet::is_subdomain(url, "printables.com")) { + std::string msg = format(_L("Download won't start. Download URL doesn't point to https://printables.com : %1%"), url); + BOOST_LOG_TRIVIAL(error) << msg; + NotificationManager* ntf_mngr = wxGetApp().notification_manager(); + ntf_mngr->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotificationLevel, msg); + return; + } + + m_downloads.emplace_back(std::make_unique(id, url, this, m_dest_folder, load_after)); + NotificationManager* ntf_mngr = wxGetApp().notification_manager(); + ntf_mngr->push_download_URL_progress_notification_with_printables_link( id + , m_downloads.back()->get_filename() + , printables_url + , std::bind(&Downloader::user_action_callback, this, std::placeholders::_1, std::placeholders::_2) + , std::bind(&GUI_App::open_link_in_printables, app, std::placeholders::_1) + ); + m_downloads.back()->start(); +} + void Downloader::on_progress(wxCommandEvent& event) { size_t id = event.GetInt(); @@ -195,14 +214,14 @@ void Downloader::on_error(wxCommandEvent& event) ntf_mngr->set_download_URL_error(id, into_u8(event.GetString())); show_error(nullptr, format_wxstr(L"%1%\n%2%", _L("The download has failed") + ":", event.GetString())); } -void Downloader::on_complete(wxCommandEvent& event) +void Downloader::on_complete(Event& event) { - // TODO: is this always true? : // here we open the file itself, notification should get 1.f progress from on progress. - set_download_state(event.GetInt(), DownloadState::DownloadDone); + set_download_state(event.data.id, DownloadState::DownloadDone); wxArrayString paths; - paths.Add(event.GetString()); - wxGetApp().plater()->load_files(paths); + paths.Add(event.data.path); + if (event.data.load_after) + wxGetApp().plater()->load_files(paths); } bool Downloader::user_action_callback(DownloaderUserAction action, int id) { diff --git a/src/slic3r/GUI/Downloader.hpp b/src/slic3r/GUI/Downloader.hpp index 4fe896f5ce..c9011fab2e 100644 --- a/src/slic3r/GUI/Downloader.hpp +++ b/src/slic3r/GUI/Downloader.hpp @@ -13,6 +13,7 @@ namespace Slic3r { namespace GUI { class NotificationManager; +class GUI_App; enum DownloadState { @@ -35,7 +36,7 @@ enum DownloaderUserAction class Download { public: - Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder); + Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder, bool load_after); void start(); void cancel(); void pause(); @@ -67,6 +68,8 @@ public: m_initialized = true; } void start_download(const std::string& full_url); + + void start_download_printables(const std::string& url, bool load_after, const std::string& printables_url, GUI_App* app); // cancel = false -> just pause bool user_action_callback(DownloaderUserAction action, int id); private: @@ -80,7 +83,7 @@ private: void on_progress(wxCommandEvent& event); void on_error(wxCommandEvent& event); - void on_complete(wxCommandEvent& event); + void on_complete(Event& event); void on_name_change(wxCommandEvent& event); void on_paused(wxCommandEvent& event); void on_canceled(wxCommandEvent& event); diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index 8cbb89abcd..7de1996e90 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -71,7 +71,7 @@ unsigned get_current_pid() } // int = DOWNLOAD ID; string = file path -wxDEFINE_EVENT(EVT_DWNLDR_FILE_COMPLETE, wxCommandEvent); +wxDEFINE_EVENT(EVT_DWNLDR_FILE_COMPLETE, Event); // int = DOWNLOAD ID; string = error msg wxDEFINE_EVENT(EVT_DWNLDR_FILE_ERROR, wxCommandEvent); // int = DOWNLOAD ID; string = progress percent @@ -97,17 +97,19 @@ struct FileGet::priv std::atomic_bool m_stopped { false }; // either canceled or paused - download is not running size_t m_written { 0 }; size_t m_absolute_size { 0 }; - priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder); + bool m_load_after; + priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder, bool load_after); void get_perform(); }; -FileGet::priv::priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder) +FileGet::priv::priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder, bool load_after) : m_id(ID) , m_url(std::move(url)) , m_filename(filename) , m_evt_handler(evt_handler) , m_dest_folder(dest_folder) + , m_load_after(load_after) { } @@ -264,23 +266,8 @@ void FileGet::priv::get_perform() m_evt_handler->QueueEvent(evt); }) .on_complete([&](std::string body, unsigned /* http_status */) { - - // TODO: perform a body size check - // - //size_t body_size = body.size(); - //if (body_size != expected_size) { - // return; - //} try { - /* - if (m_written < body.size()) - { - // this code should never be entered. As there should be on_progress call after last bit downloaded. - std::string part_for_write = body.substr(m_written); - fwrite(part_for_write.c_str(), 1, part_for_write.size(), file); - } - */ fclose(file); boost::filesystem::rename(m_tmp_path, dest_path); } @@ -294,18 +281,15 @@ void FileGet::priv::get_perform() m_evt_handler->QueueEvent(evt); return; } - - wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_COMPLETE); - evt->SetString(dest_path.wstring()); - evt->SetInt(m_id); - m_evt_handler->QueueEvent(evt); + DownloadEventData event_data = {m_id, dest_path.wstring(), m_load_after}; + wxQueueEvent(m_evt_handler, new Event(EVT_DWNLDR_FILE_COMPLETE, event_data)); }) .perform_sync(); } -FileGet::FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder) - : p(new priv(ID, std::move(url), filename, evt_handler, dest_folder)) +FileGet::FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder, bool load_after) + : p(new priv(ID, std::move(url), filename, evt_handler, dest_folder, load_after)) {} FileGet::FileGet(FileGet&& other) : p(std::move(other.p)) {} diff --git a/src/slic3r/GUI/DownloaderFileGet.hpp b/src/slic3r/GUI/DownloaderFileGet.hpp index 7859d52f62..caf94b2c26 100644 --- a/src/slic3r/GUI/DownloaderFileGet.hpp +++ b/src/slic3r/GUI/DownloaderFileGet.hpp @@ -5,6 +5,8 @@ #ifndef slic3r_DownloaderFileGet_hpp_ #define slic3r_DownloaderFileGet_hpp_ +#include "Event.hpp" + #include "../Utils/Http.hpp" #include @@ -19,7 +21,7 @@ class FileGet : public std::enable_shared_from_this { private: struct priv; public: - FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder); + FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder, bool load_after); FileGet(FileGet&& other); ~FileGet(); @@ -27,12 +29,18 @@ public: void cancel(); void pause(); void resume(); - static bool is_subdomain(const std::string& url, const std::string& domain); + static bool is_subdomain(const std::string& url, const std::string& domain); private: std::unique_ptr p; }; +struct DownloadEventData +{ + int id; + wxString path; + bool load_after; +}; // int = DOWNLOAD ID; string = file path -wxDECLARE_EVENT(EVT_DWNLDR_FILE_COMPLETE, wxCommandEvent); +wxDECLARE_EVENT(EVT_DWNLDR_FILE_COMPLETE, Event); // int = DOWNLOAD ID; string = error msg wxDECLARE_EVENT(EVT_DWNLDR_FILE_PROGRESS, wxCommandEvent); // int = DOWNLOAD ID; string = progress percent diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a9836a36f7..a938865bb9 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -4112,13 +4112,46 @@ void GUI_App::show_printer_webview_tab() mainframe->show_printer_webview_tab(preset_bundle->physical_printers.get_selected_printer_config()); } -void GUI_App::printables_request(const std::string& url) + +void GUI_App::printables_download_request(const std::string& download_url, const std::string& model_url) +{ + //this->mainframe->select_tab(size_t(0)); + + //lets always init so if the download dest folder was changed, new dest is used + boost::filesystem::path dest_folder(app_config->get("url_downloader_dest")); + if (dest_folder.empty() || !boost::filesystem::is_directory(dest_folder)) { + std::string msg = _u8L("Could not start URL download. Destination folder is not set. Please choose destination folder in Configuration Wizard."); + BOOST_LOG_TRIVIAL(error) << msg; + show_error(nullptr, msg); + return; + } + m_downloader->init(dest_folder); + m_downloader->start_download_printables(download_url, false, model_url, this); +} +void GUI_App::printables_slice_request(const std::string& download_url, const std::string& model_url) { this->mainframe->select_tab(size_t(0)); - start_download(url); + + //lets always init so if the download dest folder was changed, new dest is used + boost::filesystem::path dest_folder(app_config->get("url_downloader_dest")); + if (dest_folder.empty() || !boost::filesystem::is_directory(dest_folder)) { + std::string msg = _u8L("Could not start URL download. Destination folder is not set. Please choose destination folder in Configuration Wizard."); + BOOST_LOG_TRIVIAL(error) << msg; + show_error(nullptr, msg); + return; + } + m_downloader->init(dest_folder); + m_downloader->start_download_printables(download_url, true, model_url, this); +} +void GUI_App::printables_print_request(const std::string& download_url, const std::string& model_url) +{ + } - +void GUI_App::open_link_in_printables(const std::string& url) +{ + mainframe->show_printables_tab(url); +} bool LogGui::ignorred_message(const wxString& msg) { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 7b18a0d324..cf3c30c287 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -436,7 +436,10 @@ public: void request_project_download(std::string project_id) {} void request_open_project(std::string project_id) {} void request_remove_project(std::string project_id) {} - void printables_request(const std::string& url); + void printables_download_request(const std::string& download_url, const std::string& model_url); + void printables_slice_request(const std::string& download_url, const std::string& model_url); + void printables_print_request(const std::string& download_url, const std::string& model_url); + void open_link_in_printables(const std::string& url); private: bool on_init_inner(); void init_app_config(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ba37ccc6e7..eca7401888 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -875,7 +875,15 @@ void MainFrame::show_connect_tab(const wxString& url) m_connect_webview->set_load_default_url_on_next_error(true); m_connect_webview->load_url(url); } - +void MainFrame::show_printables_tab(const std::string& url) +{ + if (!m_printables_webview_added) { + return; + } + m_tabpanel->SetSelection(m_tabpanel->FindPage(m_printables_webview)); + m_printables_webview->set_load_default_url_on_next_error(true); + m_printables_webview->load_url_from_outside(url); +} void MainFrame::add_printables_webview_tab() { if (m_printables_webview_added) { @@ -886,8 +894,7 @@ void MainFrame::add_printables_webview_tab() wxWindow* page = m_printables_webview; const wxString text(L"Printables"); const std::string bmp_name = ""; - bool bSelect = false; - m_tabpanel->InsertNewPage(n, page, text, bmp_name, bSelect); + m_tabpanel->InsertNewPage(n, page, text, bmp_name, false); m_printables_webview->load_default_url_delayed(); m_printables_webview_added = true; } diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 2856b695f8..20614681b6 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -224,7 +224,8 @@ public: void on_account_will_refresh(); void on_account_did_refresh(const std::string& token); void on_account_logout(); - void show_connect_tab(const wxString &url); + void show_connect_tab(const wxString& url); + void show_printables_tab(const std::string& url); void add_printables_webview_tab(); void remove_printables_webview_tab(); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 4edae8d1d6..c9ed154e09 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -936,9 +936,8 @@ void NotificationManager::ProgressBarNotification::render_text(const float win_s render_cancel_button(win_size_x, win_size_y, win_pos_x, win_pos_y); render_bar(win_size_x, win_size_y, win_pos_x, win_pos_y); } - - } + void NotificationManager::ProgressBarNotification::render_bar(const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); @@ -1073,6 +1072,51 @@ void NotificationManager::ProgressBarWithCancelNotification::render_bar(const fl ImGuiPureWrap::text(text.c_str()); } +//------URLDownloadWithPrintablesLinkNotification---------------- +void NotificationManager::URLDownloadWithPrintablesLinkNotification::init() +{ + PopNotification::init(); + //m_lines_count++; + if (m_endlines.empty()) { + m_endlines.push_back(0); + } + + m_lines_count = 3; + m_multiline = true; + while (m_endlines.size() < 3) + m_endlines.push_back(m_endlines.back()); + + if(m_state == EState::Shown) + m_state = EState::NotFading; +} +bool NotificationManager::URLDownloadWithPrintablesLinkNotification::on_text_click() +{ + m_hypertext_callback_override(m_hypertext); + return false; +} +void NotificationManager::URLDownloadWithPrintablesLinkNotification::render_text(const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + assert(m_multiline); + assert(m_text1.size() >= m_endlines[0] || m_text1.size() >= m_endlines[1]); + if(m_endlines[0] > m_text1.size() || m_endlines[1] > m_text1.size()) + return; + // 1 lines text (what doesn't fit, wont show), 1 line hypertext, 1 line bar + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(m_line_height / 4); + ImGuiPureWrap::text(m_text1.substr(0, m_endlines[0]).c_str()); + + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(m_line_height + m_line_height / 4); + std::string line = _u8L("Open Printables project page"); + //ImGuiPureWrap::text(line.c_str()); + render_hypertext(m_left_indentation, m_line_height + m_line_height / 4, line); + + if (m_has_cancel_button) + render_cancel_button(win_size_x, win_size_y, win_pos_x, win_pos_y); + render_bar(win_size_x, win_size_y, win_pos_x, win_pos_y); +} + + //------URLDownloadNotification---------------- void NotificationManager::URLDownloadNotification::render_close_button(const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) @@ -2495,6 +2539,19 @@ void NotificationManager::push_download_URL_progress_notification(size_t id, con push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, id, user_action_callback), 0); } +void NotificationManager::push_download_URL_progress_notification_with_printables_link(size_t id, const std::string& text, const std::string& url, std::function user_action_callback, std::function hypertext_callback) +{ + // If already exists + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::URLDownload && dynamic_cast(notification.get())->get_download_id() == id) { + return; + } + } + // push new one + NotificationData data{ NotificationType::URLDownload, NotificationLevel::ProgressBarNotificationLevel, 30, _u8L("Download") + ": " + text, url }; + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, id, user_action_callback, hypertext_callback), 0); +} + void NotificationManager::set_download_URL_progress(size_t id, float percentage) { for (std::unique_ptr& notification : m_pop_notifications) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 22e48c51d4..0c6390f2ec 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -244,6 +244,7 @@ public: void set_download_progress_percentage(float percentage); // Download URL progress notif void push_download_URL_progress_notification(size_t id, const std::string& text, std::function user_action_callback); + void push_download_URL_progress_notification_with_printables_link(size_t id, const std::string& text, const std::string& url, std::function user_action_callback, std::function hypertext_callback); void set_download_URL_progress(size_t id, float percentage); void set_download_URL_paused(size_t id); void set_download_URL_canceled(size_t id); @@ -577,6 +578,22 @@ private: std::string m_error_message; }; + class URLDownloadWithPrintablesLinkNotification : public URLDownloadNotification + { + public: + URLDownloadWithPrintablesLinkNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, size_t download_id, std::function user_action_callback, std::function hypertext_callback) + : URLDownloadNotification(n, id_provider, evt_handler, download_id, user_action_callback) + , m_hypertext_callback_override(hypertext_callback) + { + } + protected: + void render_text(const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y) override; + void init() override; + bool on_text_click() override; + std::function m_hypertext_callback_override; + }; + class PrintHostUploadNotification : public ProgressBarNotification { public: diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index 8c3c2f781a..3279fb5c5f 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -639,7 +639,7 @@ void LoginWebViewDialog::on_navigation_request(wxWebViewEvent &evt) { wxString url = evt.GetURL(); if (url.starts_with(L"prusaslicer")) { - delete_cookies(m_browser, "https://account.prusa3d.com"); + delete_cookies(m_browser, Utils::ServiceConfig::instance().account_url()); delete_cookies(m_browser, "https://accounts.google.com"); delete_cookies(m_browser, "https://appleid.apple.com"); delete_cookies(m_browser, "https://facebook.com"); diff --git a/src/slic3r/GUI/WebViewPanel.cpp b/src/slic3r/GUI/WebViewPanel.cpp index 3329658c81..9ec5767ce4 100644 --- a/src/slic3r/GUI/WebViewPanel.cpp +++ b/src/slic3r/GUI/WebViewPanel.cpp @@ -190,8 +190,7 @@ void WebViewPanel::on_show(wxShowEvent& evt) m_shown = evt.IsShown(); if (evt.IsShown() && m_load_default_url) { m_load_default_url = false; - //load_url(m_default_url); - load_error_page(); + load_url(m_default_url); } } @@ -239,6 +238,8 @@ void WebViewPanel::on_back_button(wxCommandEvent& WXUNUSED(evt)) { if (!m_browser) return; + if (!m_browser->CanGoBack()) + return; m_browser->GoBack(); } @@ -249,6 +250,8 @@ void WebViewPanel::on_forward_button(wxCommandEvent& WXUNUSED(evt)) { if (!m_browser) return; + if (!m_browser->CanGoForward()) + return; m_browser->GoForward(); } @@ -469,7 +472,7 @@ case type: \ WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_OTHER); } - BOOST_LOG_TRIVIAL(error) << this <<" WebViewPanel error: " << category; + BOOST_LOG_TRIVIAL(error) << this <<" WebViewPanel error: " << category << " url: " << evt.GetURL(); load_error_page(); #ifdef DEBUG_URL_PANEL m_info->ShowMessage(wxString("An error occurred loading ") + evt.GetURL() + "\n" + @@ -857,9 +860,14 @@ PrintablesWebViewPanel::PrintablesWebViewPanel(wxWindow* parent) : WebViewPanel(parent, GUI::from_u8(Utils::ServiceConfig::instance().printables_url()), { "ExternalApp" }, "other_loading_reload", "other_connection_failed") { m_browser->Bind(wxEVT_WEBVIEW_LOADED, &PrintablesWebViewPanel::on_loaded, this); + m_events["accessTokenExpired"] = std::bind(&PrintablesWebViewPanel::on_printables_event_access_token_expired, this, std::placeholders::_1); - m_events["printGcode"] = std::bind(&PrintablesWebViewPanel::on_printables_event_print_gcode, this, std::placeholders::_1); m_events["reloadHomePage"] = std::bind(&PrintablesWebViewPanel::on_reload_event, this, std::placeholders::_1); + m_events["printGcode"] = std::bind(&PrintablesWebViewPanel::on_printables_event_print_gcode, this, std::placeholders::_1); + m_events["downloadFile"] = std::bind(&PrintablesWebViewPanel::on_printables_event_download_file, this, std::placeholders::_1); + m_events["sliceFile"] = std::bind(&PrintablesWebViewPanel::on_printables_event_slice_file, this, std::placeholders::_1); + m_events["requiredLogin"] = std::bind(&PrintablesWebViewPanel::on_printables_event_required_login, this, std::placeholders::_1); + } void PrintablesWebViewPanel::handle_message(const std::string& message) @@ -888,50 +896,16 @@ void PrintablesWebViewPanel::handle_message(const std::string& message) m_events[event_string](message); } } -void PrintablesWebViewPanel::on_printables_event_access_token_expired(const std::string& message_data) -{ - // accessTokenExpired - // Printables pozaduje refresh access tokenu, muze byt volano nekolikrat.Nechme na Mobilni aplikaci at zaridi to ze zareaguje jen jednou -} -void PrintablesWebViewPanel::on_printables_event_print_gcode(const std::string& message_data) -{ - // printGcode - // Uzivatel kliknul na tlacitko tisk u gcode. Dalsi posilane atributy jsou url, material, printer, nozzlediam -} -void PrintablesWebViewPanel::on_reload_event(const std::string& message_data) -{ - load_default_url(); -} -/* -Eventy Slicer -> Printables -accessTokenWillChange -WebUI zavola event predtim nez udela refresh access tokenu proti Prusa Accountu na Printables to bude znamenat pozastaveni requestu Mobile app muze chtit udelat refresh i bez explicitni predchozi printables zadosti skrz accessTokenExpired event -accessTokenChange -window postMessage JSON stringify { event 'accessTokenChange' token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC' } -volani po uspesne rotaci tokenu -historyBack -navigace zpet triggerovana z mobilni aplikace -historyForward -navigace vpred triggerovana z mobilni aplikace -*/ void PrintablesWebViewPanel::on_navigation_request(wxWebViewEvent &evt) { const wxString url = evt.GetURL(); - // download with url - if (url.StartsWith(L"prusaslicer")) { - evt.Veto(); - wxGetApp().printables_request(into_u8(url)); - return; - } if (url.StartsWith(m_default_url)) { m_reached_default_url = true; } else if (m_reached_default_url) { BOOST_LOG_TRIVIAL(info) << evt.GetURL() << " does not start with default url. Vetoing."; evt.Veto(); } - - } void PrintablesWebViewPanel::load_default_url() @@ -1055,7 +1029,7 @@ void PrintablesWebViewPanel::logout() #endif // } -void PrintablesWebViewPanel::login(const std::string access_token) +void PrintablesWebViewPanel::login(const std::string& access_token) { if (!m_shown) { return; @@ -1076,7 +1050,7 @@ void PrintablesWebViewPanel::login(const std::string access_token) run_script("window.location.reload();"); } -void PrintablesWebViewPanel::send_refreshed_token(const std::string access_token) +void PrintablesWebViewPanel::send_refreshed_token(const std::string& access_token) { if (m_load_default_url) { return; @@ -1097,6 +1071,11 @@ void PrintablesWebViewPanel::send_will_refresh() run_script(script); } +void PrintablesWebViewPanel::load_url_from_outside(const std::string& url) +{ + load_url(from_u8(Utils::ServiceConfig::instance().printables_url() + url)); +} + void PrintablesWebViewPanel::on_script_message(wxWebViewEvent& evt) { BOOST_LOG_TRIVIAL(error) << "received message from Printables: " << evt.GetString(); @@ -1111,4 +1090,157 @@ void PrintablesWebViewPanel::sys_color_changed() WebViewPanel::sys_color_changed(); } +void PrintablesWebViewPanel::on_printables_event_access_token_expired(const std::string& message_data) +{ + // accessTokenExpired + // Printables pozaduje refresh access tokenu, muze byt volano nekolikrat.Nechme na Mobilni aplikaci at zaridi to ze zareaguje jen jednou + + // We do no react on this event now - Our Acount managment should know when to renew our tokens. +} + +void PrintablesWebViewPanel::on_reload_event(const std::string& message_data) +{ + // Event from our error / loading html pages + load_default_url(); +} +void PrintablesWebViewPanel::on_printables_event_print_gcode(const std::string& message_data) +{ + BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< " " << message_data; +} +void PrintablesWebViewPanel::on_printables_event_download_file(const std::string& message_data) +{ + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " " << message_data; + // { "event": "downloadFile", "url": "https://media.printables.com/somesecure.stl", "modelUrl": "https://www.printables.com/model/123" } + std::string download_url; + std::string model_url; + try { + std::stringstream ss(message_data); + pt::ptree ptree; + pt::read_json(ss, ptree); + if (const auto url = ptree.get_optional("url"); url) { + download_url = *url; + } + if (const auto url = ptree.get_optional("modelUrl"); url) { + model_url = *url; + } + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(error) << "Could not parse printables message. " << e.what(); + return; + } + assert(!download_url.empty() && !model_url.empty()); + boost::filesystem::path url_path(download_url); + show_download_notification(url_path.filename().string()); + wxGetApp().printables_download_request(download_url, model_url); +} +void PrintablesWebViewPanel::on_printables_event_slice_file(const std::string& message_data) +{ + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " " << message_data; + // { "event": "sliceFile", "url": "https://media.printables.com/somesecure.zip", "modelUrl": "https://www.printables.com/model/123" } + std::string download_url; + std::string model_url; + try { + std::stringstream ss(message_data); + pt::ptree ptree; + pt::read_json(ss, ptree); + if (const auto url = ptree.get_optional("url"); url) { + download_url = *url; + } + if (const auto url = ptree.get_optional("modelUrl"); url) { + model_url = *url; + } + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(error) << "Could not parse printables message. " << e.what(); + return; + } + assert(!download_url.empty() && !model_url.empty()); + wxGetApp().printables_slice_request(download_url, model_url); +} + +void PrintablesWebViewPanel::on_printables_event_required_login(const std::string& message_data) +{ + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " " << message_data; +} + +void PrintablesWebViewPanel::show_download_notification(const std::string& filename) +{ + std::string message = GUI::format(_u8L("Downloading %1%"),filename); + std::string script = GUI::format(R"( + // Inject custom CSS + var style = document.createElement('style'); + style.innerHTML = ` + body { + /* Add your body styles here */ + } + .notification-popup { + position: fixed; + right: 10px; + bottom: 10px; + background-color: #333333; /* Dark background */ + padding: 10px; + border-radius: 6px; /* Slightly rounded corners */ + color: #ffffff; /* White text */ + font-family: Arial, sans-serif; + font-size: 12px; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.3); /* Add a subtle shadow */ + min-width: 350px; /* Ensure it has a minimum width */ + min-height: 50px; + } + + .notification-popup a:hover { + text-decoration: underline; /* Underline on hover */ + } + .notification-popup .close-button { + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid #ffa500; /* Orange border for the button */ + border-radius: 4px; + text-align: center; + font-size: 16px; + line-height: 16px; + cursor: pointer; + padding-top: 1px; + } + .notification-popup .close-button:hover { + background-color: #ffa500; /* Orange background on hover */ + color: #333333; /* Dark color for the "X" on hover */ + } + .notification-popup .close-button:before { + content: 'X'; + color: #ffa500; /* Orange "X" */ + font-weight: bold; + } + `; + document.head.appendChild(style); + + // Define the notification functions + function appendNotification() { + const body = document.getElementsByTagName('body')[0]; + const notifDiv = document.createElement('div'); + notifDiv.innerHTML = ` +
+ PrusaSlicer: %1% +
+ `; + notifDiv.className = 'notification-popup'; + notifDiv.id = 'slicer-notification'; + body.appendChild(notifDiv); + + window.setTimeout(removeNotification, 5000); + } + + function removeNotification() { + const notifDiv = document.getElementById('slicer-notification'); + if (notifDiv) + notifDiv.remove(); + } + + appendNotification(); +)", message); + run_script(script); +} + } // namespace slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/WebViewPanel.hpp b/src/slic3r/GUI/WebViewPanel.hpp index bddb30cb6a..be9e181f22 100644 --- a/src/slic3r/GUI/WebViewPanel.hpp +++ b/src/slic3r/GUI/WebViewPanel.hpp @@ -180,20 +180,36 @@ public: void sys_color_changed() override; void logout(); - void login(const std::string access_token); - void send_refreshed_token(const std::string access_token); + void login(const std::string& access_token); + void send_refreshed_token(const std::string& access_token); void send_will_refresh(); + void load_url_from_outside(const std::string& url); private: void handle_message(const std::string& message); void on_printables_event_access_token_expired(const std::string& message_data); - void on_printables_event_print_gcode(const std::string& message_data); void on_reload_event(const std::string& message_data); - + void on_printables_event_print_gcode(const std::string& message_data); + void on_printables_event_download_file(const std::string& message_data); + void on_printables_event_slice_file(const std::string& message_data); + void on_printables_event_required_login(const std::string& message_data); void load_default_url() override; std::string get_url_lang_theme(const wxString& url); + void show_download_notification(const std::string& filename); std::map> m_events; +/* +Eventy Slicer -> Printables +accessTokenWillChange +WebUI zavola event predtim nez udela refresh access tokenu proti Prusa Accountu na Printables to bude znamenat pozastaveni requestu Mobile app muze chtit udelat refresh i bez explicitni predchozi printables zadosti skrz accessTokenExpired event +accessTokenChange +window postMessage JSON stringify { event 'accessTokenChange' token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC' } +volani po uspesne rotaci tokenu +historyBack +navigace zpet triggerovana z mobilni aplikace +historyForward +navigace vpred triggerovana z mobilni aplikace +*/ }; } // namespace Slic3r::GUI