Warning screen, smarter license re-validation, version reporting
Warning screen - New reusable WarningPage (word-wrapped message, ENTER acknowledges and continues to the screen you were about to enter). - First use: pressing Play while the selected audio is still downloading. Dynamic Audio requires every file; a fixed track requires its own file. License re-validation - No longer re-verifies on every Home render. Checks now run only when the email/license key is entered or changed, on the manual option, at startup when already due, and hourly while valid. - Invalid + unchanged credentials are never re-checked (persisted pair). - Valid re-checks at most once per hour, anchored on a DB timestamp so an overdue check runs immediately after a power cycle. - New system_kv keys track the checked pair, outcome, and last-check time. - Added a System Settings row "Manually Validate License Key OK|Invalid" (OK green, Invalid red, label white) that runs a check immediately. Portal / local integration - License validation request now sends the app version as `appVersion`. - The entered license key is mirrored to ~/.local/radiantwave/license.txt. Misc - "Please connect to WiFi" only shows when no WiFi is joined AND the internet is unreachable (fixes the NetworkManager-missing and Ethernet cases). - Added unit tests for the audio-download warning, license scheduling policy, license request shape, and the new settings-menu entry.
This commit is contained in:
@@ -261,6 +261,35 @@ std::string computeFileMd5(const std::string& path) {
|
||||
return hex;
|
||||
}
|
||||
|
||||
bool buildAudioDownloadWarning(const AudioDownloadSnapshot& snapshot,
|
||||
bool dynamicAudio,
|
||||
const std::string& selectedFilename,
|
||||
std::string& message) {
|
||||
if (dynamicAudio) {
|
||||
if (snapshot.allAvailable) return false;
|
||||
message =
|
||||
"You currently have 'Dynamic Audio' selected, but some audio "
|
||||
"files are still being downloaded. Some audio will not play until "
|
||||
"these files have finished downloading.";
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& file : snapshot.files) {
|
||||
if (file.filename != selectedFilename) continue;
|
||||
if (file.available) return false;
|
||||
const std::string name =
|
||||
file.display_name.empty() ? file.filename : file.display_name;
|
||||
message = "You currently have '" + name +
|
||||
"' selected, but it is still being downloaded. No audio "
|
||||
"will play until this file has finished downloading.";
|
||||
return true;
|
||||
}
|
||||
|
||||
// The selection is not part of the manifest (the manifest may not be known
|
||||
// yet), so the downloader cannot confirm whether it is still downloading.
|
||||
return false;
|
||||
}
|
||||
|
||||
// ─── Lifecycle ──────────────────────────────────────────────────────────
|
||||
|
||||
AudioDownloader::~AudioDownloader() {
|
||||
@@ -289,6 +318,30 @@ std::string AudioDownloader::statusString() const {
|
||||
return status_;
|
||||
}
|
||||
|
||||
AudioDownloadSnapshot AudioDownloader::snapshot() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return snapshot_;
|
||||
}
|
||||
|
||||
void AudioDownloader::publishSnapshot(
|
||||
const std::vector<AudioManifestEntry>& manifest,
|
||||
const std::unordered_set<std::string>& availableFilenames) {
|
||||
AudioDownloadSnapshot snap;
|
||||
snap.manifestKnown = true;
|
||||
snap.allAvailable = !manifest.empty();
|
||||
snap.files.reserve(manifest.size());
|
||||
for (const auto& entry : manifest) {
|
||||
const bool available = availableFilenames.count(entry.filename) != 0;
|
||||
const std::string displayName =
|
||||
entry.display_name.empty() ? humanizeFilename(entry.filename)
|
||||
: entry.display_name;
|
||||
snap.files.push_back({entry.filename, displayName, available});
|
||||
if (!available) snap.allAvailable = false;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
snapshot_ = std::move(snap);
|
||||
}
|
||||
|
||||
void AudioDownloader::setStatus(const std::string& msg) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -359,6 +412,11 @@ void AudioDownloader::run() {
|
||||
|
||||
size_t toDownload = queue.size();
|
||||
size_t downloaded = 0;
|
||||
|
||||
// Publish what is (and is not) on disk before downloads begin so the
|
||||
// UI can warn immediately, then again as each file lands.
|
||||
publishSnapshot(manifest, listed);
|
||||
|
||||
if (!queue.empty()) {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(dir, ec);
|
||||
@@ -373,6 +431,7 @@ void AudioDownloader::run() {
|
||||
// Publish immediately so a file that just finished downloading
|
||||
// appears in the list without waiting for the rest.
|
||||
syncNow();
|
||||
publishSnapshot(manifest, listed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user