diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 407749ef83..761b016d2f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1538,6 +1538,15 @@ bool GUI_App::on_init_inner() this->check_updates(false); }); + Bind(EVT_CONFIG_UPDATER_FAILED_ARCHIVE, [this](const wxCommandEvent& evt) { + assert(!evt.GetString().empty()); + // TRN Notification text, 1 is list of vendors. + std::string notification_text = format(_u8L("Update Check Failed for the Following Vendors:\n\n%1%\nThis may be due to an account logout or a lost connection. Please verify your account status and internet connection. Then select \"Check for Configuration Updates\" to repeat."), evt.GetString()); + notification_manager()->push_notification(NotificationType::FailedSecretVendorUpdateSync, + NotificationManager::NotificationLevel::WarningNotificationLevel, + notification_text); + }); + Bind(wxEVT_ACTIVATE_APP, [this](const wxActivateEvent &evt) { if (plater_) { if (auto user_account = plater_->get_user_account()) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 215aae7b5e..96e49f162b 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -146,6 +146,8 @@ enum class NotificationType SupportNozzleDiameterDiffer, // Transient error on Prusa Account communication - user is informed and has option to cancel (logout) AccountTransientRetry, + // Failed to download secret repo archive + FailedSecretVendorUpdateSync }; class NotificationManager diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 202c35989e..64a57a9323 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -971,6 +971,7 @@ void Plater::priv::init() std::string text = format(_u8L("Logged to Prusa Account as %1%."), username); // login notification this->notification_manager->close_notification_of_type(NotificationType::UserAccountID); + this->notification_manager->close_notification_of_type(NotificationType::FailedSecretVendorUpdateSync); // show connect tab this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text); diff --git a/src/slic3r/GUI/PresetArchiveDatabase.cpp b/src/slic3r/GUI/PresetArchiveDatabase.cpp index 26cb49971e..a499a790c5 100644 --- a/src/slic3r/GUI/PresetArchiveDatabase.cpp +++ b/src/slic3r/GUI/PresetArchiveDatabase.cpp @@ -786,6 +786,7 @@ void PresetArchiveDatabase::read_server_manifest(const std::string& json_body) ) == m_archive_repositories.end()) { ArchiveRepository::RepositoryManifest manifest(repo_ptr->get_manifest()); + manifest.not_in_manifest = true; m_archive_repositories.emplace_back(std::make_unique(repo_ptr->get_uuid(), std::move(manifest))); } } @@ -809,8 +810,7 @@ SharedArchiveRepositoryVector PresetArchiveDatabase::get_selected_archive_reposi { SharedArchiveRepositoryVector result; result.reserve(m_archive_repositories.size()); - for (const auto &repo_ptr : m_archive_repositories) - { + for (const auto &repo_ptr : m_archive_repositories) { auto it = m_selected_repositories_uuid.find(repo_ptr->get_uuid()); assert(it != m_selected_repositories_uuid.end()); if (it->second) { diff --git a/src/slic3r/GUI/PresetArchiveDatabase.hpp b/src/slic3r/GUI/PresetArchiveDatabase.hpp index 359df8fbef..d3643b914a 100644 --- a/src/slic3r/GUI/PresetArchiveDatabase.hpp +++ b/src/slic3r/GUI/PresetArchiveDatabase.hpp @@ -37,6 +37,7 @@ public: // not read from manifest json boost::filesystem::path tmp_path; // Where archive is unzziped. Created each app run. boost::filesystem::path source_path; // Path given by user. Stored between app runs. + bool not_in_manifest {false}; RepositoryManifest() = default; RepositoryManifest( @@ -47,7 +48,8 @@ public: const std::string &description = "", const std::string &visibility = "", const boost::filesystem::path &tmp_path = "", - const boost::filesystem::path &source_path = "" + const boost::filesystem::path &source_path = "", + bool not_in_manifest = false ) : id(id) , name(name) @@ -57,6 +59,7 @@ public: , visibility(visibility) , tmp_path(tmp_path) , source_path(source_path) + , not_in_manifest(not_in_manifest) {} RepositoryManifest(const RepositoryManifest &other) : id(other.id) @@ -66,7 +69,8 @@ public: , description(other.description) , visibility(other.visibility) , tmp_path(other.tmp_path) - , source_path(other.source_path) + , source_path(other.source_path) + , not_in_manifest(other.not_in_manifest) {} }; // Use std::move when calling constructor. diff --git a/src/slic3r/GUI/UpdatesUIManager.cpp b/src/slic3r/GUI/UpdatesUIManager.cpp index 8878e92b06..3322e5d88e 100644 --- a/src/slic3r/GUI/UpdatesUIManager.cpp +++ b/src/slic3r/GUI/UpdatesUIManager.cpp @@ -115,7 +115,7 @@ void RepositoryUpdateUIManager::fill_entries(bool init_selection/* = false*/) if (data.source_path.empty()) { // online repo - m_online_entries.push_back({ is_selected, uuid, data.name, data.description, data.visibility }); + m_online_entries.push_back({ is_selected, uuid, data.name, data.description, data.visibility, data.not_in_manifest }); } else { // offline repo @@ -161,12 +161,19 @@ void RepositoryUpdateUIManager::fill_grids() }); add(chb); - if (entry.visibility.empty()) - add(new wxStaticText(m_parent, wxID_ANY, "")); - else { + if (entry.not_in_manifest) { + wxStaticBitmap* bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("notification_warning")); + // TRN tooltip in Configuration Wizard - Configuration Sources + bmp->SetToolTip(_L("This source has installed vendors, yet you do not have rights to receive updates of it.\n" + "This may be because you are logged out. Log in to restore access to all your subscribed sources.\n" + "If you are logged in, please concider unsubscribing this source.")); + add(bmp); + } else if (!entry.visibility.empty()) { wxStaticBitmap* bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("info")); bmp->SetToolTip(from_u8(entry.visibility)); add(bmp); + } else { + add(new wxStaticText(m_parent, wxID_ANY, "")); } add(new wxStaticText(m_parent, wxID_ANY, from_u8(entry.name) + " ")); diff --git a/src/slic3r/GUI/UpdatesUIManager.hpp b/src/slic3r/GUI/UpdatesUIManager.hpp index 38878bb494..2515e19c39 100644 --- a/src/slic3r/GUI/UpdatesUIManager.hpp +++ b/src/slic3r/GUI/UpdatesUIManager.hpp @@ -21,14 +21,15 @@ namespace GUI { class RepositoryUpdateUIManager { struct OnlineEntry { - OnlineEntry(bool use, const std::string &id, const std::string &name, const std::string &description, const std::string &visibility) : - use(use), id(id), name(name), description(description), visibility(visibility) {} + OnlineEntry(bool use, const std::string &id, const std::string &name, const std::string &description, const std::string &visibility, bool not_in_manifest) : + use(use), id(id), name(name), description(description), visibility(visibility) ,not_in_manifest(not_in_manifest) {} bool use; std::string id; std::string name; std::string description; std::string visibility; + bool not_in_manifest; }; struct OfflineEntry { diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index f23ac74b11..b8e9ad3c1f 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -311,7 +311,8 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const ArchiveRep // Download profiles archive zip fs::path archive_path(cache_path / "vendor_indices.zip"); if (!archive_repository->get_archive(archive_path, ui_status)) { - BOOST_LOG_TRIVIAL(error) << "Download of vedor profiles archive zip of " << archive_repository->get_manifest().id << " repository has failed."; + BOOST_LOG_TRIVIAL(error) << "Download of vendor profiles archive zip of " << archive_repository->get_manifest().id << " repository has failed."; + ui_status->add_failed_archive(archive_repository->get_manifest().id); return; } if (ui_status->get_canceled()) { diff --git a/src/slic3r/Utils/PresetUpdaterWrapper.cpp b/src/slic3r/Utils/PresetUpdaterWrapper.cpp index 645e2be1cf..3baac5887a 100644 --- a/src/slic3r/Utils/PresetUpdaterWrapper.cpp +++ b/src/slic3r/Utils/PresetUpdaterWrapper.cpp @@ -1,6 +1,7 @@ #include "PresetUpdaterWrapper.hpp" #include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" #include "slic3r/GUI/format.hpp" @@ -15,6 +16,43 @@ namespace Slic3r { wxDEFINE_EVENT(EVT_PRESET_UPDATER_STATUS_END, PresetUpdaterStatusSimpleEvent); wxDEFINE_EVENT(EVT_PRESET_UPDATER_STATUS_PRINT, PresetUpdaterStatusMessageEvent); wxDEFINE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent); +wxDEFINE_EVENT(EVT_CONFIG_UPDATER_FAILED_ARCHIVE, wxCommandEvent); + + +namespace { +// Returns string of vendors that failed archive download. divided by new line +std::string proccess_failed_archives(const std::vector& failed_archives, const VendorMap& vendors, const SharedArchiveRepositoryVector &repos) +{ + std::string failed_vendors; + for (const std::string& failed_archive : failed_archives) { + // find if failed_archive is secret + if (const auto it = + std::find_if(repos.begin(), repos.end(), + [failed_archive](const auto* rep){ + return rep->get_manifest().id == failed_archive; + }) + ; it != repos.end()) + { + // add all installed vendors of failed_archive + for (const auto& pair :vendors) { + if (pair.second.repo_id == failed_archive) { + failed_vendors += pair.second.name + "\n"; + } + } + } + } + return failed_vendors; +} +void display_failed_vendors_dialog(wxWindow *parent, const std::string& failed_vendors) +{ + // TRN Dialog text, 1 is list of vendors. + std::string dialog_text = format(_u8L("Update Check Failed for the Following Vendors:\n\n%1%\n" + "This may be because you are logged out. Log in to restore access to all your subscribed sources.\n" + "If you are logged in and a vendor is failing, it may no longer be available in your subscribed sources."), failed_vendors); + GUI::WarningDialog dialog(parent, dialog_text, _L("Update Check Failed"), wxOK); + dialog.ShowModal(); +} +} PresetUpdaterWrapper::PresetUpdaterWrapper() : m_preset_updater(std::make_unique()) @@ -83,6 +121,13 @@ bool PresetUpdaterWrapper::wizard_sync(const PresetBundle* preset_bundle, const return false; } + // Find secret vendors that failed to download idx in archive + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + std::string failed_vendors = proccess_failed_archives(m_ui_status->get_failed_archives(), vendors_copy, repos); + if (!failed_vendors.empty()) { + display_failed_vendors_dialog(parent, failed_vendors); + } + // Offer update installation. if (full_sync) { const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); @@ -158,6 +203,14 @@ PresetUpdater::UpdateResult PresetUpdaterWrapper::check_updates_on_user_request( GUI::ErrorDialog err_msg(nullptr, failed_paths, false); err_msg.ShowModal(); } + + // Find secret vendors that failed to download idx in archive + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + std::string failed_vendors = proccess_failed_archives(m_ui_status->get_failed_archives(), vendors_copy, repos); + if (!failed_vendors.empty()) { + display_failed_vendors_dialog(parent, failed_vendors); + } + // preset_updater::config_update does show wxDialog updater_result = m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_TEXT_BOX, m_preset_archive_database->get_selected_archive_repositories(), m_ui_status.get()); return updater_result; @@ -207,6 +260,14 @@ void PresetUpdaterWrapper::sync_preset_updater(wxEvtHandler* end_evt_handler, co if (this->m_ui_status->get_canceled()) { return; } wxCommandEvent* evt = new wxCommandEvent(EVT_CONFIG_UPDATER_SYNC_DONE); wxQueueEvent(end_evt_handler, evt); + + // Find secret vendors that failed to download idx in archive + std::string failed_vendors = proccess_failed_archives(m_ui_status->get_failed_archives(), vendors_copy, repos); + if (!failed_vendors.empty()) { + wxCommandEvent* evt_arch = new wxCommandEvent(EVT_CONFIG_UPDATER_FAILED_ARCHIVE); + evt_arch->SetString(GUI::from_u8(failed_vendors)); + wxQueueEvent(end_evt_handler, evt_arch); + } }; m_worker_thread = std::thread(worker_body); @@ -244,6 +305,7 @@ void PresetUpdaterUIStatus::reset(PresetUpdaterUIStatus::PresetUpdaterRetryPolic m_evt_handler = nullptr; m_error_msg.clear(); m_target.clear(); + m_failed_archives.clear(); } bool PresetUpdaterUIStatus::on_attempt(int attempt, unsigned delay) diff --git a/src/slic3r/Utils/PresetUpdaterWrapper.hpp b/src/slic3r/Utils/PresetUpdaterWrapper.hpp index 328b71de20..f10553728c 100644 --- a/src/slic3r/Utils/PresetUpdaterWrapper.hpp +++ b/src/slic3r/Utils/PresetUpdaterWrapper.hpp @@ -25,6 +25,8 @@ using PresetUpdaterStatusMessageEvent = GUI::Event; wxDECLARE_EVENT(EVT_PRESET_UPDATER_STATUS_END, PresetUpdaterStatusSimpleEvent); wxDECLARE_EVENT(EVT_PRESET_UPDATER_STATUS_PRINT, PresetUpdaterStatusMessageEvent); wxDECLARE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent); +wxDECLARE_EVENT(EVT_CONFIG_UPDATER_FAILED_ARCHIVE, wxCommandEvent); + class PresetBundle; class Semver; @@ -55,6 +57,8 @@ public: HttpRetryOpt get_retry_policy() const { return m_retry_policy; } std::string get_error() const { return m_error_msg; } std::string get_target() const { return m_target; } + void add_failed_archive(const std::string& id) { m_failed_archives.emplace_back(id); } + const std::vector& get_failed_archives() { return m_failed_archives; } // called from PresetUpdaterUIStatusCancel (ui thread) void set_canceled(bool val) { m_canceled.store(val); } @@ -68,6 +72,8 @@ private: HttpRetryOpt m_retry_policy; static const std::map policy_map; + + std::vector m_failed_archives; }; // Purpose of this class: