[feat] show sponsorship prompt once per version

This commit is contained in:
dijunkun
2026-10-19 23:06:14 +08:00
parent e7a49361f7
commit 8a7de23c7f
6 changed files with 31 additions and 23 deletions
@@ -52,6 +52,10 @@ int ConfigCenter::Load() {
ini_->Delete(section_, "enable_desktop_quality_optimization"); ini_->Delete(section_, "enable_desktop_quality_optimization");
persist_config_migration |= persist_config_migration |=
ini_->Delete(section_, "enable_minimize_to_tray"); ini_->Delete(section_, "enable_minimize_to_tray");
// The old global flag has no version information. Prompt once for the
// current release, then remember its version when the user dismisses it.
persist_config_migration |=
ini_->Delete(section_, "sponsorship_prompt_dismissed");
const long language_value = const long language_value =
ini_->GetLongValue(section_, "language", static_cast<long>(language_)); ini_->GetLongValue(section_, "language", static_cast<long>(language_));
@@ -131,8 +135,8 @@ int ConfigCenter::Load() {
portable_service_prompt_suppressed_ = portable_service_prompt_suppressed_ =
ini_->GetBoolValue(section_, "portable_service_prompt_suppressed", ini_->GetBoolValue(section_, "portable_service_prompt_suppressed",
portable_service_prompt_suppressed_); portable_service_prompt_suppressed_);
sponsorship_prompt_dismissed_ = sponsorship_prompt_dismissed_version_ =
ini_->GetBoolValue(section_, "sponsorship_prompt_dismissed", false); ini_->GetValue(section_, "sponsorship_prompt_dismissed_version", "");
const char* file_transfer_save_path_value = const char* file_transfer_save_path_value =
ini_->GetValue(section_, "file_transfer_save_path", nullptr); ini_->GetValue(section_, "file_transfer_save_path", nullptr);
@@ -223,8 +227,8 @@ int ConfigCenter::Save() {
save_remote_previews_.load()); save_remote_previews_.load());
ini_->SetBoolValue(section_, "portable_service_prompt_suppressed", ini_->SetBoolValue(section_, "portable_service_prompt_suppressed",
portable_service_prompt_suppressed_); portable_service_prompt_suppressed_);
ini_->SetBoolValue(section_, "sponsorship_prompt_dismissed", ini_->SetValue(section_, "sponsorship_prompt_dismissed_version",
sponsorship_prompt_dismissed_); sponsorship_prompt_dismissed_version_.c_str());
ini_->SetValue(section_, "file_transfer_save_path", ini_->SetValue(section_, "file_transfer_save_path",
file_transfer_save_path_.c_str()); file_transfer_save_path_.c_str());
@@ -399,12 +403,13 @@ int ConfigCenter::SetPortableServicePromptSuppressed(bool suppressed) {
return 0; return 0;
} }
int ConfigCenter::SetSponsorshipPromptDismissed(bool dismissed) { int ConfigCenter::SetSponsorshipPromptDismissedVersion(
if (StoreValues({{"sponsorship_prompt_dismissed", const std::string& version) {
dismissed ? "true" : "false"}}) != 0) { if (version.empty() ||
StoreValues({{"sponsorship_prompt_dismissed_version", version}}) != 0) {
return -1; return -1;
} }
sponsorship_prompt_dismissed_ = dismissed; sponsorship_prompt_dismissed_version_ = version;
return 0; return 0;
} }
@@ -476,8 +481,8 @@ bool ConfigCenter::IsPortableServicePromptSuppressed() const {
return portable_service_prompt_suppressed_; return portable_service_prompt_suppressed_;
} }
bool ConfigCenter::IsSponsorshipPromptDismissed() const { std::string ConfigCenter::GetSponsorshipPromptDismissedVersion() const {
return sponsorship_prompt_dismissed_; return sponsorship_prompt_dismissed_version_;
} }
int ConfigCenter::SetFileTransferSavePath(const std::string& path) { int ConfigCenter::SetFileTransferSavePath(const std::string& path) {
@@ -49,7 +49,7 @@ class ConfigCenter {
int SetPrivacyScreen(bool enable_privacy_screen); int SetPrivacyScreen(bool enable_privacy_screen);
int SetSaveRemotePreviews(bool enabled); int SetSaveRemotePreviews(bool enabled);
int SetPortableServicePromptSuppressed(bool suppressed); int SetPortableServicePromptSuppressed(bool suppressed);
int SetSponsorshipPromptDismissed(bool dismissed); int SetSponsorshipPromptDismissedVersion(const std::string& version);
int SetFileTransferSavePath(const std::string& path); int SetFileTransferSavePath(const std::string& path);
// read config // read config
@@ -74,7 +74,7 @@ class ConfigCenter {
bool IsEnablePrivacyScreen() const; bool IsEnablePrivacyScreen() const;
bool IsSaveRemotePreviews() const; bool IsSaveRemotePreviews() const;
bool IsPortableServicePromptSuppressed() const; bool IsPortableServicePromptSuppressed() const;
bool IsSponsorshipPromptDismissed() const; std::string GetSponsorshipPromptDismissedVersion() const;
std::string GetFileTransferSavePath() const; std::string GetFileTransferSavePath() const;
int Load(); int Load();
@@ -104,7 +104,7 @@ class ConfigCenter {
std::atomic<bool> enable_privacy_screen_{true}; std::atomic<bool> enable_privacy_screen_{true};
std::atomic<bool> save_remote_previews_{true}; std::atomic<bool> save_remote_previews_{true};
bool portable_service_prompt_suppressed_ = false; bool portable_service_prompt_suppressed_ = false;
bool sponsorship_prompt_dismissed_ = false; std::string sponsorship_prompt_dismissed_version_;
std::string file_transfer_save_path_ = ""; std::string file_transfer_save_path_ = "";
}; };
} // namespace crossdesk } // namespace crossdesk
@@ -1267,7 +1267,7 @@ void GuiApplication::InitializeUi() {
#endif #endif
ui_ = std::make_unique<SlintUi>(); ui_ = std::make_unique<SlintUi>();
ui_->sponsorship_prompt_pending = ui_->sponsorship_prompt_pending =
!config_center_->IsSponsorshipPromptDismissed(); config_center_->GetSponsorshipPromptDismissedVersion() != CROSSDESK_VERSION;
ui_->video_presenter = ui_->video_presenter =
std::make_unique<SlintVideoPresenter>(*video_renderer_); std::make_unique<SlintVideoPresenter>(*video_renderer_);
#if _WIN32 #if _WIN32
@@ -1752,7 +1752,7 @@ void GuiApplication::BindMainCallbacks(bool gallery_window) {
main->on_dismiss_sponsorship([this, main] { main->on_dismiss_sponsorship([this, main] {
main->set_sponsorship_open(false); main->set_sponsorship_open(false);
ui_->sponsorship_prompt_pending = false; ui_->sponsorship_prompt_pending = false;
if (config_center_->SetSponsorshipPromptDismissed(true) != 0) { if (config_center_->SetSponsorshipPromptDismissedVersion(CROSSDESK_VERSION) != 0) {
LOG_WARN("Could not save sponsorship prompt dismissal"); LOG_WARN("Could not save sponsorship prompt dismissal");
} }
}); });
@@ -293,9 +293,9 @@ struct TranslationRow {
X(sponsorship_message, u8"如果 CrossDesk 对你有帮助,欢迎赞助支持项目的持续开发、跨平台测试与日常维护。感谢你的支持!", \ X(sponsorship_message, u8"如果 CrossDesk 对你有帮助,欢迎赞助支持项目的持续开发、跨平台测试与日常维护。感谢你的支持!", \
"If CrossDesk is useful to you, consider supporting its continued development, cross-platform testing, and maintenance. Thank you for your support!", \ "If CrossDesk is useful to you, consider supporting its continued development, cross-platform testing, and maintenance. Thank you for your support!", \
u8"Если CrossDesk вам полезен, поддержите его дальнейшую разработку, тестирование на разных платформах и сопровождение. Спасибо за вашу поддержку!") \ u8"Если CrossDesk вам полезен, поддержите его дальнейшую разработку, тестирование на разных платформах и сопровождение. Спасибо за вашу поддержку!") \
X(sponsorship_dismiss_hint, u8"关闭后不再自动弹出,你仍可在「关于」中找到赞助入口。", \ X(sponsorship_dismiss_hint, u8"关闭后本版本不再提示,升级后再次显示。赞助入口仍在「关于」中。", \
"This prompt will not appear again after you close it. Sponsorship links remain available in About.", \ "Once closed, this prompt stays hidden until the version changes. Sponsorship links remain available in About.", \
u8"После закрытия это окно больше не появится автоматически. Ссылки для поддержки останутся в разделе «О программе».") \ u8"После закрытия это окно появится только при смене версии. Ссылки для поддержки останутся в разделе «О программе».") \
X(sponsor_via_afdian, u8"爱发电", "AFDIAN", "AFDIAN") \ X(sponsor_via_afdian, u8"爱发电", "AFDIAN", "AFDIAN") \
X(notification, u8"通知", "Notification", u8"Уведомление") \ X(notification, u8"通知", "Notification", u8"Уведомление") \
X(new_version, u8"新版本", "New version", u8"Новая версия") \ X(new_version, u8"新版本", "New version", u8"Новая версия") \
+6 -4
View File
@@ -2289,15 +2289,16 @@ export component MainWindow inherits Window {
width: min(480px, parent.width - 40px); width: min(480px, parent.width - 40px);
height: self.content-top + sponsorship-content.preferred-height + 24px; height: self.content-top + sponsorship-content.preferred-height + 24px;
title: UiStrings.sponsorship-title; title: UiStrings.sponsorship-title;
close-label: MessageStrings.close; show-close-button: false;
close => { root.dismiss-sponsorship(); }
sponsorship-content := VerticalLayout { sponsorship-content := VerticalLayout {
x: 24px; y: parent.content-top; x: 24px; y: parent.content-top;
width: parent.width - 48px; height: self.preferred-height; width: parent.width - 48px; height: self.preferred-height;
padding: 0px; spacing: 18px; padding: 0px; spacing: 20px;
Text { Text {
text: UiStrings.sponsorship-message; text: UiStrings.sponsorship-message;
color: WorkbenchStyle.text; font-size: 13px; wrap: word-wrap; color: WorkbenchStyle.text; font-size: 14px; wrap: word-wrap;
// Match message bodies' 1.5em line height for wrapped text.
font-family: "CrossDesk Message Spacing";
} }
SponsorshipSection { SponsorshipSection {
show-title: false; show-title: false;
@@ -2308,6 +2309,7 @@ export component MainWindow inherits Window {
Text { Text {
text: UiStrings.sponsorship-dismiss-hint; text: UiStrings.sponsorship-dismiss-hint;
color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap; color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap;
font-family: "CrossDesk Message Spacing";
} }
Rectangle { height: 1px; background: WorkbenchStyle.separator; } Rectangle { height: 1px; background: WorkbenchStyle.separator; }
HorizontalLayout { HorizontalLayout {
@@ -49,6 +49,7 @@ export component SettingsButton inherits Rectangle {
export component SettingsDialog inherits DialogSurface { export component SettingsDialog inherits DialogSurface {
in property <string> title; in property <string> title;
in property <string> close-label; in property <string> close-label;
in property <bool> show-close-button: true;
in property <int> badge-count: 0; in property <int> badge-count: 0;
in property <string> badge-label; in property <string> badge-label;
in property <string> header-status; in property <string> header-status;
@@ -92,7 +93,7 @@ export component SettingsDialog inherits DialogSurface {
clicked => { root.action(); } clicked => { root.action(); }
action-label-metrics := Text { visible: false; text: root.action-text; font-size: 12px; } action-label-metrics := Text { visible: false; text: root.action-text; font-size: 12px; }
} }
IconButton { if root.show-close-button: IconButton {
width: 28px; height: 28px; width: 28px; height: 28px;
icon: FontAwesomeIcons.xmark; icon-size: 14px; icon: FontAwesomeIcons.xmark; icon-size: 14px;
icon-color: WorkbenchStyle.muted; icon-color: WorkbenchStyle.muted;