DonationNag: ask after each major update (#10213)

This changes how the donation nag timing works.

The donation nag will now appear:

- after a major update (e.g. 48 -> 49)*
- once in late july
- once in december

however, a donation nag will never pop up more than once a month. So, if there is an update on the 26th of November, and you get a popup on the 28th, you will not get one in december.

This is of course still disableable in your config.
This commit is contained in:
Vaxry 2025-04-30 14:47:35 +02:00 committed by GitHub
parent b2ad21a65c
commit 54c89104de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 78 additions and 25 deletions

View File

@ -10,7 +10,9 @@
#include "../helpers/fs/FsUtils.hpp" #include "../helpers/fs/FsUtils.hpp"
#include <hyprutils/os/Process.hpp> #include <hyprutils/os/Process.hpp>
#include <hyprutils/string/VarList.hpp>
using namespace Hyprutils::OS; using namespace Hyprutils::OS;
using namespace Hyprutils::String;
constexpr const char* LAST_NAG_FILE_NAME = "lastNag"; constexpr const char* LAST_NAG_FILE_NAME = "lastNag";
constexpr uint64_t DAY_IN_SECONDS = 3600ULL * 24; constexpr uint64_t DAY_IN_SECONDS = 3600ULL * 24;
@ -46,29 +48,28 @@ CDonationNagManager::CDonationNagManager() {
const auto EPOCH = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); const auto EPOCH = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
const auto LASTNAGSTR = NFsUtils::readFileAsString(*DATAROOT + "/" + LAST_NAG_FILE_NAME); uint64_t currentMajor = 0;
try {
if (!LASTNAGSTR) { CVarList vl(HYPRLAND_VERSION, 0, '.');
const auto EPOCHSTR = std::format("{}", EPOCH); currentMajor = std::stoull(vl[1]);
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR); } catch (...) {
// ????
return; return;
} }
uint64_t LAST_EPOCH = 0; auto state = getState();
try { if ((!state.major && currentMajor <= 48) || !state.epoch) {
LAST_EPOCH = std::stoull(*LASTNAGSTR); state.major = currentMajor;
} catch (std::exception& e) { state.epoch = state.epoch == 0 ? EPOCH : state.epoch;
Debug::log(ERR, "DonationNag: Last epoch invalid? Failed to parse \"{}\". Setting to today.", *LASTNAGSTR); writeState(state);
const auto EPOCHSTR = std::format("{}", EPOCH);
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR);
return; return;
} }
// don't nag if the last nag was less than a month ago. This is // don't nag if the last nag was less than a month ago. This is
// mostly for first-time nags, as other nags happen in specific time frames shorter than a month // mostly for first-time nags, as other nags happen in specific time frames shorter than a month
if (EPOCH - LAST_EPOCH < MONTH_IN_SECONDS) { if (EPOCH - state.epoch < MONTH_IN_SECONDS) {
Debug::log(LOG, "DonationNag: last nag was {} days ago, too early for a nag.", (int)std::round((EPOCH - LAST_EPOCH) / (double)MONTH_IN_SECONDS)); Debug::log(LOG, "DonationNag: last nag was {} days ago, too early for a nag.", (int)std::round((EPOCH - state.epoch) / (double)DAY_IN_SECONDS));
return; return;
} }
@ -92,23 +93,66 @@ CDonationNagManager::CDonationNagManager() {
Debug::log(LOG, "DonationNag: hit nag month {} days {}-{}, it's {} today, nagging", MONTH, nagPoint.dayStart, nagPoint.dayEnd, DAY); Debug::log(LOG, "DonationNag: hit nag month {} days {}-{}, it's {} today, nagging", MONTH, nagPoint.dayStart, nagPoint.dayEnd, DAY);
m_bFired = true; fire();
const auto EPOCHSTR = std::format("{}", EPOCH); state.major = currentMajor;
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR); state.epoch = EPOCH;
writeState(state);
g_pEventLoopManager->doLater([] {
CProcess proc("hyprland-donate-screen", {});
proc.runAsync();
});
break; break;
} }
if (!m_bFired) if (!m_bFired)
Debug::log(LOG, "DonationNag: didn't hit any nagging periods"); Debug::log(LOG, "DonationNag: didn't hit any nagging periods, checking update");
if (state.major < currentMajor) {
Debug::log(LOG, "DonationNag: hit nag for major update {} -> {}", state.major, currentMajor);
fire();
state.major = currentMajor;
state.epoch = EPOCH;
writeState(state);
}
if (!m_bFired)
Debug::log(LOG, "DonationNag: didn't hit nagging conditions");
} }
bool CDonationNagManager::fired() { bool CDonationNagManager::fired() {
return m_bFired; return m_bFired;
} }
void CDonationNagManager::fire() {
static const auto DATAROOT = NFsUtils::getDataHome();
m_bFired = true;
g_pEventLoopManager->doLater([] {
CProcess proc("hyprland-donate-screen", {});
proc.runAsync();
});
}
CDonationNagManager::SStateData CDonationNagManager::getState() {
static const auto DATAROOT = NFsUtils::getDataHome();
const auto STR = NFsUtils::readFileAsString(*DATAROOT + "/" + LAST_NAG_FILE_NAME);
if (!STR.has_value())
return {};
CVarList lines(*STR, 0, '\n');
CDonationNagManager::SStateData state;
try {
state.epoch = std::stoull(lines[0]);
state.major = std::stoull(lines[1]);
} catch (...) { ; }
return state;
}
void CDonationNagManager::writeState(const SStateData& s) {
static const auto DATAROOT = NFsUtils::getDataHome();
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, std::format("{}\n{}", s.epoch, s.major));
}

View File

@ -10,7 +10,16 @@ class CDonationNagManager {
bool fired(); bool fired();
private: private:
bool m_bFired = false; struct SStateData {
uint64_t epoch = 0;
uint64_t major = 0;
};
SStateData getState();
void writeState(const SStateData& s);
void fire();
bool m_bFired = false;
}; };
inline UP<CDonationNagManager> g_pDonationNagManager; inline UP<CDonationNagManager> g_pDonationNagManager;