2025-01-10 19:09:40 +01:00
|
|
|
#include "DonationNagManager.hpp"
|
|
|
|
|
#include "../debug/Log.hpp"
|
|
|
|
|
#include "VersionKeeperManager.hpp"
|
|
|
|
|
#include "eventLoop/EventLoopManager.hpp"
|
|
|
|
|
#include "../config/ConfigValue.hpp"
|
|
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <format>
|
|
|
|
|
|
|
|
|
|
#include "../helpers/fs/FsUtils.hpp"
|
|
|
|
|
|
|
|
|
|
#include <hyprutils/os/Process.hpp>
|
2025-04-30 14:47:35 +02:00
|
|
|
#include <hyprutils/string/VarList.hpp>
|
2025-01-10 19:09:40 +01:00
|
|
|
using namespace Hyprutils::OS;
|
2025-04-30 14:47:35 +02:00
|
|
|
using namespace Hyprutils::String;
|
2025-01-10 19:09:40 +01:00
|
|
|
|
|
|
|
|
constexpr const char* LAST_NAG_FILE_NAME = "lastNag";
|
|
|
|
|
constexpr uint64_t DAY_IN_SECONDS = 3600ULL * 24;
|
|
|
|
|
constexpr uint64_t MONTH_IN_SECONDS = DAY_IN_SECONDS * 30;
|
|
|
|
|
|
|
|
|
|
struct SNagDatePoint {
|
|
|
|
|
// Counted from 1, as in Jan 1st is 1, 1
|
|
|
|
|
// No month-boundaries because I am lazy
|
|
|
|
|
uint8_t month = 0, dayStart = 0, dayEnd = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
|
const std::vector<SNagDatePoint> NAG_DATE_POINTS = {
|
|
|
|
|
SNagDatePoint {
|
|
|
|
|
7, 20, 31,
|
|
|
|
|
},
|
|
|
|
|
SNagDatePoint {
|
|
|
|
|
12, 1, 28
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
|
|
CDonationNagManager::CDonationNagManager() {
|
|
|
|
|
static auto PNONAG = CConfigValue<Hyprlang::INT>("ecosystem:no_donation_nag");
|
|
|
|
|
|
|
|
|
|
if (g_pVersionKeeperMgr->fired() || *PNONAG)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const auto DATAROOT = NFsUtils::getDataHome();
|
|
|
|
|
|
|
|
|
|
if (!DATAROOT)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const auto EPOCH = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
|
|
|
|
2025-04-30 14:47:35 +02:00
|
|
|
uint64_t currentMajor = 0;
|
|
|
|
|
try {
|
|
|
|
|
CVarList vl(HYPRLAND_VERSION, 0, '.');
|
|
|
|
|
currentMajor = std::stoull(vl[1]);
|
|
|
|
|
} catch (...) {
|
|
|
|
|
// ????
|
2025-01-10 19:09:40 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 14:47:35 +02:00
|
|
|
auto state = getState();
|
2025-01-10 19:09:40 +01:00
|
|
|
|
2025-04-30 14:47:35 +02:00
|
|
|
if ((!state.major && currentMajor <= 48) || !state.epoch) {
|
|
|
|
|
state.major = currentMajor;
|
|
|
|
|
state.epoch = state.epoch == 0 ? EPOCH : state.epoch;
|
|
|
|
|
writeState(state);
|
2025-01-10 19:09:40 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2025-04-30 14:47:35 +02:00
|
|
|
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 - state.epoch) / (double)DAY_IN_SECONDS));
|
2025-01-10 19:09:40 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!NFsUtils::executableExistsInPath("hyprland-donate-screen")) {
|
|
|
|
|
Debug::log(ERR, "DonationNag: executable doesn't exist, skipping.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
|
|
|
auto local = *localtime(&tt);
|
|
|
|
|
|
|
|
|
|
const auto MONTH = local.tm_mon + 1;
|
|
|
|
|
const auto DAY = local.tm_mday;
|
|
|
|
|
|
|
|
|
|
for (const auto& nagPoint : NAG_DATE_POINTS) {
|
|
|
|
|
if (MONTH != nagPoint.month)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (DAY < nagPoint.dayStart || DAY > nagPoint.dayEnd)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Debug::log(LOG, "DonationNag: hit nag month {} days {}-{}, it's {} today, nagging", MONTH, nagPoint.dayStart, nagPoint.dayEnd, DAY);
|
|
|
|
|
|
2025-04-30 14:47:35 +02:00
|
|
|
fire();
|
2025-01-10 19:09:40 +01:00
|
|
|
|
2025-04-30 14:47:35 +02:00
|
|
|
state.major = currentMajor;
|
|
|
|
|
state.epoch = EPOCH;
|
|
|
|
|
writeState(state);
|
2025-01-10 19:09:40 +01:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 17:07:20 +02:00
|
|
|
if (!m_fired)
|
2025-04-30 14:47:35 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 17:07:20 +02:00
|
|
|
if (!m_fired)
|
2025-04-30 14:47:35 +02:00
|
|
|
Debug::log(LOG, "DonationNag: didn't hit nagging conditions");
|
2025-01-10 19:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDonationNagManager::fired() {
|
2025-05-02 17:07:20 +02:00
|
|
|
return m_fired;
|
2025-04-30 14:47:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDonationNagManager::fire() {
|
|
|
|
|
static const auto DATAROOT = NFsUtils::getDataHome();
|
|
|
|
|
|
2025-05-02 17:07:20 +02:00
|
|
|
m_fired = true;
|
2025-04-30 14:47:35 +02:00
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|