Files
Ly-sec 2f735eda81 ChangelogPanel: nice formatting for changelogs
AboutTab: update version connection
GitHubService: cleanup, move changelog logic to UpdateService
UpdateService: use new changelog host
2025-11-21 11:01:59 +01:00

188 lines
5.6 KiB
QML

pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
// GitHub API logic for contributors
Singleton {
id: root
property string githubDataFile: Quickshell.env("NOCTALIA_GITHUB_FILE") || (Settings.cacheDir + "github.json")
property int githubUpdateFrequency: 60 * 60 // 1 hour expressed in seconds
property bool isFetchingData: false
readonly property alias data: adapter // Used to access via GitHubService.data.xxx.yyy
// Public properties for easy access
property string latestVersion: I18n.tr("system.unknown-version")
property var contributors: []
FileView {
id: githubDataFileView
path: githubDataFile
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
Component.onCompleted: {
reload();
}
onLoaded: {
loadFromCache();
}
onLoadFailed: function (error) {
if (error.toString().includes("No such file") || error === 2) {
// Fetch data after a short delay to ensure file is created
Qt.callLater(() => {
fetchFromGitHub();
});
}
}
JsonAdapter {
id: adapter
property string version: I18n.tr("system.unknown-version")
property var contributors: []
property real timestamp: 0
}
}
// --------------------------------
function loadFromCache() {
const now = Time.timestamp;
var needsRefetch = false;
if (!data.timestamp || (now >= data.timestamp + githubUpdateFrequency)) {
needsRefetch = true;
Logger.d("GitHub", "Cache expired or missing, scheduling fetch");
} else {
Logger.d("GitHub", "Loading cached GitHub data (age:", Math.round((now - data.timestamp) / 60), "minutes)");
}
if (data.version) {
root.latestVersion = data.version;
}
if (data.contributors) {
root.contributors = data.contributors;
}
if (needsRefetch) {
fetchFromGitHub();
}
}
// --------------------------------
function fetchFromGitHub() {
if (isFetchingData) {
Logger.w("GitHub", "GitHub data is still fetching");
return;
}
isFetchingData = true;
versionProcess.running = true;
contributorsProcess.running = true;
}
// --------------------------------
function saveData() {
data.timestamp = Time.timestamp;
Logger.d("GitHub", "Saving data to cache file:", githubDataFile);
Logger.d("GitHub", "Data to save - version:", data.version, "contributors:", data.contributors.length);
// Ensure cache directory exists
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]);
Qt.callLater(() => {
// Use direct ID reference to the FileView
githubDataFileView.writeAdapter();
Logger.d("GitHub", "Cache file written successfully");
});
}
// --------------------------------
function checkAndSaveData() {
// Only save when all processes are finished
if (!versionProcess.running && !contributorsProcess.running) {
root.isFetchingData = false;
root.saveData();
}
}
// --------------------------------
function resetCache() {
data.version = I18n.tr("system.unknown-version");
data.contributors = [];
data.timestamp = 0;
// Try to fetch immediately
fetchFromGitHub();
}
Process {
id: versionProcess
command: ["curl", "-s", "https://api.github.com/repos/noctalia-dev/noctalia-shell/releases/latest"]
stdout: StdioCollector {
onStreamFinished: {
try {
const response = text;
if (response && response.trim()) {
const data = JSON.parse(response);
if (data.tag_name) {
const version = data.tag_name;
root.data.version = version;
root.latestVersion = version;
Logger.d("GitHub", "Latest version fetched from GitHub:", version);
} else if (data.message) {
Logger.w("GitHub", "Latest release fetch warning:", data.message);
} else {
Logger.w("GitHub", "No tag_name in GitHub response");
}
} else {
Logger.w("GitHub", "Empty response from GitHub API");
}
} catch (e) {
Logger.e("GitHub", "Failed to parse version:", e);
}
// Check if both processes are done
checkAndSaveData();
}
}
}
Process {
id: contributorsProcess
command: ["curl", "-s", "https://api.github.com/repos/noctalia-dev/noctalia-shell/contributors?per_page=100"]
stdout: StdioCollector {
onStreamFinished: {
try {
const response = text;
Logger.d("GitHub", "Raw contributors response length:", response ? response.length : 0);
if (response && response.trim()) {
const data = JSON.parse(response);
Logger.d("GitHub", "Parsed contributors data type:", typeof data, "length:", Array.isArray(data) ? data.length : "not array");
root.data.contributors = data || [];
root.contributors = root.data.contributors;
Logger.d("GitHub", "Contributors fetched from GitHub:", root.contributors.length);
} else {
Logger.w("GitHub", "Empty response from GitHub API for contributors");
root.data.contributors = [];
root.contributors = [];
}
} catch (e) {
Logger.e("GitHub", "Failed to parse contributors:", e);
root.data.contributors = [];
root.contributors = [];
}
// Check if both processes are done
checkAndSaveData();
}
}
}
}