diff --git a/Modules/Settings/Tabs/About.qml b/Modules/Settings/Tabs/About.qml index 3c97970..58e5eb1 100644 --- a/Modules/Settings/Tabs/About.qml +++ b/Modules/Settings/Tabs/About.qml @@ -8,330 +8,316 @@ import qs.Services import qs.Widgets ColumnLayout { - id: root + id: root - property string latestVersion: Github.latestVersion - property string currentVersion: "v1.2.1" // Fallback version - property var contributors: Github.contributors + property string latestVersion: Github.latestVersion + property string currentVersion: "v1.2.1" // Fallback version + property var contributors: Github.contributors + Component.onCompleted: { + // Initialize the Github service + Github.init() + } + + spacing: 0 + Layout.fillWidth: true + Layout.fillHeight: true + + Process { + id: currentVersionProcess + + command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"] Component.onCompleted: { - // Initialize the Github service - Github.init(); + running = true } - spacing: 0 + stdout: StdioCollector { + onStreamFinished: { + const version = text.trim() + if (version && version !== "Unknown") { + root.currentVersion = version + } else { + currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"] + currentVersionProcess.running = true + } + } + } + } + + ScrollView { + id: scrollView + Layout.fillWidth: true Layout.fillHeight: true + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded - Process { - id: currentVersionProcess + ColumnLayout { + width: scrollView.availableWidth + spacing: 0 - command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"] - Component.onCompleted: { - running = true; + NText { + text: "Noctalia: quiet by design" + font.pointSize: 24 * Scaling.scale(screen) + font.weight: Style.fontWeightBold + color: Colors.textPrimary + Layout.alignment: Qt.AlignCenter + Layout.bottomMargin: 8 * Scaling.scale(screen) + } + + NText { + text: "It may just be another quickshell setup but it won't get in your way." + font.pointSize: 14 * Scaling.scale(screen) + color: Colors.textSecondary + Layout.alignment: Qt.AlignCenter + Layout.bottomMargin: 16 * Scaling.scale(screen) + } + + GridLayout { + Layout.alignment: Qt.AlignCenter + columns: 2 + rowSpacing: 4 + columnSpacing: 8 + + NText { + text: "Latest Version:" + font.pointSize: 16 * Scaling.scale(screen) + color: Colors.textSecondary + Layout.alignment: Qt.AlignRight } - stdout: StdioCollector { - onStreamFinished: { - const version = text.trim(); - if (version && version !== "Unknown") { - root.currentVersion = version; - } else { - currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"]; - currentVersionProcess.running = true; - } - } + NText { + text: root.latestVersion + font.pointSize: 16 * Scaling.scale(screen) + color: Colors.textPrimary + font.weight: Style.fontWeightBold } - } + NText { + text: "Installed Version:" + font.pointSize: 16 * Scaling.scale(screen) + color: Colors.textSecondary + Layout.alignment: Qt.AlignRight + } - ScrollView { - id: scrollView + NText { + text: root.currentVersion + font.pointSize: 16 * Scaling.scale(screen) + color: Colors.textPrimary + font.weight: Style.fontWeightBold + } + } + Rectangle { + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 8 + Layout.preferredWidth: updateText.implicitWidth + 46 + Layout.preferredHeight: 32 + radius: 20 + color: updateArea.containsMouse ? Colors.accentPrimary : "transparent" + border.color: Colors.accentPrimary + border.width: 1 + visible: { + if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") + return false + + const latest = root.latestVersion.replace("v", "").split(".") + const current = root.currentVersion.replace("v", "").split(".") + for (var i = 0; i < Math.max(latest.length, current.length); i++) { + const l = parseInt(latest[i] || "0") + const c = parseInt(current[i] || "0") + if (l > c) + return true + + if (l < c) + return false + } + return false + } + + RowLayout { + anchors.centerIn: parent + spacing: 8 + + NText { + text: "system_update" + font.family: "Material Symbols Outlined" + font.pointSize: 18 * Scaling.scale(screen) + color: updateArea.containsMouse ? Colors.backgroundPrimary : Colors.accentPrimary + } + + NText { + id: updateText + + text: "Download latest release" + font.pointSize: 14 * Scaling.scale(screen) + color: updateArea.containsMouse ? Colors.backgroundPrimary : Colors.accentPrimary + } + } + + MouseArea { + id: updateArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]) + } + } + } + + // Separator + Rectangle { Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Colors.outline + opacity: 0.3 + } + + NText { + text: "Contributors" + font.pointSize: 18 * Scaling.scale(screen) + font.weight: Style.fontWeightBold + color: Colors.textPrimary + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 32 + } + + NText { + text: "(" + root.contributors.length + ")" + font.pointSize: 14 * Scaling.scale(screen) + color: Colors.textSecondary + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 4 + } + + ScrollView { + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 200 * 4 Layout.fillHeight: true - padding: 16 - rightPadding: 12 + Layout.topMargin: 16 clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.vertical.policy: ScrollBar.AsNeeded - ColumnLayout { - width: scrollView.availableWidth - spacing: 0 + GridView { + id: contributorsGrid - NText { - text: "Noctalia: quiet by design" - font.pointSize: 24 * Scaling.scale(screen) - font.weight: Style.fontWeightBold - color: Colors.textPrimary - Layout.alignment: Qt.AlignCenter - Layout.bottomMargin: 8 * Scaling.scale(screen) - } + anchors.fill: parent + width: 200 * 4 + height: Math.ceil(root.contributors.length / 4) * 100 + cellWidth: 200 + cellHeight: 100 + model: root.contributors - NText { - text: "It may just be another quickshell setup but it won't get in your way." - font.pointSize: 14 * Scaling.scale(screen) - color: Colors.textSecondary - Layout.alignment: Qt.AlignCenter - Layout.bottomMargin: 16 * Scaling.scale(screen) - } + delegate: Rectangle { + width: contributorsGrid.cellWidth - 16 + height: contributorsGrid.cellHeight - 4 + radius: 20 + color: contributorArea.containsMouse ? Colors.hover : "transparent" - GridLayout { - Layout.alignment: Qt.AlignCenter - columns: 2 - rowSpacing: 4 - columnSpacing: 8 + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 12 - NText { - text: "Latest Version:" - font.pointSize: 16 * Scaling.scale(screen) - color: Colors.textSecondary - Layout.alignment: Qt.AlignRight - } + Item { + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 - NText { - text: root.latestVersion - font.pointSize: 16 * Scaling.scale(screen) - color: Colors.textPrimary - font.weight: Style.fontWeightBold - } + Image { + id: avatarImage - NText { - text: "Installed Version:" - font.pointSize: 16 * Scaling.scale(screen) - color: Colors.textSecondary - Layout.alignment: Qt.AlignRight - } - - NText { - text: root.currentVersion - font.pointSize: 16 * Scaling.scale(screen) - color: Colors.textPrimary - font.weight: Style.fontWeightBold - } - - } - - Rectangle { - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 8 - Layout.preferredWidth: updateText.implicitWidth + 46 - Layout.preferredHeight: 32 - radius: 20 - color: updateArea.containsMouse ? Colors.accentPrimary : "transparent" - border.color: Colors.accentPrimary - border.width: 1 - visible: { - if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") - return false; - - const latest = root.latestVersion.replace("v", "").split("."); - const current = root.currentVersion.replace("v", "").split("."); - for (let i = 0; i < Math.max(latest.length, current.length); i++) { - const l = parseInt(latest[i] || "0"); - const c = parseInt(current[i] || "0"); - if (l > c) - return true; - - if (l < c) - return false; + anchors.fill: parent + source: modelData.avatar_url || "" + sourceSize: Qt.size(80, 80) + visible: false + mipmap: true + smooth: true + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + onStatusChanged: { + if (status === Image.Error) { + console.log("[About] Failed to load avatar for", modelData.login, "URL:", modelData.avatar_url) } - return false; + } } - RowLayout { - anchors.centerIn: parent - spacing: 8 - - NText { - text: "system_update" - font.family: "Material Symbols Outlined" - font.pointSize: 18 * Scaling.scale(screen) - color: updateArea.containsMouse ? Colors.backgroundPrimary : Colors.accentPrimary - } - - NText { - id: updateText - - text: "Download latest release" - font.pointSize: 14 * Scaling.scale(screen) - color: updateArea.containsMouse ? Colors.backgroundPrimary : Colors.accentPrimary - } - + MultiEffect { + anchors.fill: parent + source: avatarImage + maskEnabled: true + maskSource: mask } - MouseArea { - id: updateArea + Item { + id: mask + anchors.fill: parent + layer.enabled: true + visible: false + + Rectangle { anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]); - } + radius: parent.width / 2 + } } - } + NText { + anchors.centerIn: parent + text: "person" + font.family: "Material Symbols Outlined" + font.pointSize: 24 * Scaling.scale(screen) + color: contributorArea.containsMouse ? Colors.backgroundPrimary : Colors.textPrimary + visible: !avatarImage.source || avatarImage.status !== Image.Ready + } + } - // Separator - Rectangle { + ColumnLayout { + spacing: 4 + Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Colors.outline - opacity: 0.3 - } - - NText { - text: "Contributors" - font.pointSize: 18 * Scaling.scale(screen) - font.weight: Style.fontWeightBold - color: Colors.textPrimary - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 32 - } - - NText { - text: "(" + root.contributors.length + ")" - font.pointSize: 14 * Scaling.scale(screen) - color: Colors.textSecondary - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 4 - } - - ScrollView { - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: 200 * 4 - Layout.fillHeight: true - Layout.topMargin: 16 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - GridView { - id: contributorsGrid - - anchors.fill: parent - width: 200 * 4 - height: Math.ceil(root.contributors.length / 4) * 100 - cellWidth: 200 - cellHeight: 100 - model: root.contributors - - delegate: Rectangle { - width: contributorsGrid.cellWidth - 16 - height: contributorsGrid.cellHeight - 4 - radius: 20 - color: contributorArea.containsMouse ? Colors.hover : "transparent" - - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 12 - - Item { - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: 40 - Layout.preferredHeight: 40 - - Image { - id: avatarImage - - anchors.fill: parent - source: modelData.avatar_url || "" - sourceSize: Qt.size(80, 80) - visible: false - mipmap: true - smooth: true - asynchronous: true - fillMode: Image.PreserveAspectCrop - cache: true - - onStatusChanged: { - if (status === Image.Error) { - console.log("[About] Failed to load avatar for", modelData.login, "URL:", modelData.avatar_url); - } - } - } - - MultiEffect { - anchors.fill: parent - source: avatarImage - maskEnabled: true - maskSource: mask - } - - Item { - id: mask - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - anchors.fill: parent - radius: parent.width / 2 - } - } - - NText { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pointSize: 24 * Scaling.scale(screen) - color: contributorArea.containsMouse ? Colors.backgroundPrimary : Colors.textPrimary - visible: !avatarImage.source || avatarImage.status !== Image.Ready - } - - } - - ColumnLayout { - spacing: 4 - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - - NText { - text: modelData.login || "Unknown" - font.pointSize: 13 * Scaling.scale(screen) - color: contributorArea.containsMouse ? Colors.backgroundPrimary : Colors.textPrimary - elide: Text.ElideRight - Layout.fillWidth: true - } - - NText { - text: (modelData.contributions || 0) + " commits" - font.pointSize: 11 * Scaling.scale(screen) - color: contributorArea.containsMouse ? Colors.backgroundPrimary : Colors.textSecondary - } - - } - - } - - MouseArea { - id: contributorArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if (modelData.html_url) - Quickshell.execDetached(["xdg-open", modelData.html_url]); - - } - } - - } + NText { + text: modelData.login || "Unknown" + font.pointSize: 13 * Scaling.scale(screen) + color: contributorArea.containsMouse ? Colors.backgroundPrimary : Colors.textPrimary + elide: Text.ElideRight + Layout.fillWidth: true } + NText { + text: (modelData.contributions || 0) + " commits" + font.pointSize: 11 * Scaling.scale(screen) + color: contributorArea.containsMouse ? Colors.backgroundPrimary : Colors.textSecondary + } + } } + MouseArea { + id: contributorArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (modelData.html_url) + Quickshell.execDetached(["xdg-open", modelData.html_url]) + } + } + } } - + } } - + } } diff --git a/Services/Github.qml b/Services/Github.qml index 4d6e8f2..a1409a6 100644 --- a/Services/Github.qml +++ b/Services/Github.qml @@ -32,12 +32,12 @@ Singleton { onLoadFailed: function (error) { if (error.toString().includes("No such file") || error === 2) { // File doesn't exist, create it with default values - console.log("[Github] Creating new cache file..."); + console.log("[Github] Creating new cache file...") writeAdapter() // Fetch data after a short delay to ensure file is created Qt.callLater(() => { - fetchFromGitHub() - }) + fetchFromGitHub() + }) } } @@ -51,26 +51,25 @@ Singleton { } // -------------------------------- - function init() { - // does nothing but ensure the singleton is created + function init() {// does nothing but ensure the singleton is created // do not remove } // -------------------------------- function loadFromCache() { - const now = Date.now(); + const now = Date.now() if (!data.timestamp || (now - data.timestamp > githubUpdateFrequency * 1000)) { - console.log("[Github] Cache expired or missing, fetching new data from GitHub..."); - fetchFromGitHub(); - return; + console.log("[Github] Cache expired or missing, fetching new data from GitHub...") + fetchFromGitHub() + return } - console.log("[Github] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)"); - + console.log("[Github] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)") + if (data.version) { - root.latestVersion = data.version; + root.latestVersion = data.version } if (data.contributors) { - root.contributors = data.contributors; + root.contributors = data.contributors } } @@ -82,20 +81,20 @@ Singleton { } isFetchingData = true - versionProcess.running = true; - contributorsProcess.running = true; + versionProcess.running = true + contributorsProcess.running = true } // -------------------------------- function saveData() { - data.timestamp = Date.now(); + data.timestamp = Date.now() Qt.callLater(() => { - // Access the FileView's writeAdapter method - var fileView = root.children.find(child => child.objectName === "githubDataFileView"); - if (fileView) { - fileView.writeAdapter(); - } - }); + // Access the FileView's writeAdapter method + var fileView = root.children.find(child => child.objectName === "githubDataFileView") + if (fileView) { + fileView.writeAdapter() + } + }) } // -------------------------------- @@ -116,26 +115,26 @@ Singleton { stdout: StdioCollector { onStreamFinished: { try { - const response = text; + const response = text if (response && response.trim()) { - const data = JSON.parse(response); + const data = JSON.parse(response) if (data.tag_name) { - const version = data.tag_name; - root.data.version = version; - root.latestVersion = version; - console.log("[Github] Latest version fetched from GitHub:", version); + const version = data.tag_name + root.data.version = version + root.latestVersion = version + console.log("[Github] Latest version fetched from GitHub:", version) } else { - console.log("[Github] No tag_name in GitHub response"); + console.log("[Github] No tag_name in GitHub response") } } else { - console.log("[Github] Empty response from GitHub API"); + console.log("[Github] Empty response from GitHub API") } } catch (e) { - console.error("[Github] Failed to parse version:", e); + console.error("[Github] Failed to parse version:", e) } - + // Check if both processes are done - checkAndSaveData(); + checkAndSaveData() } } } @@ -148,25 +147,25 @@ Singleton { stdout: StdioCollector { onStreamFinished: { try { - const response = text; + const response = text if (response && response.trim()) { - const data = JSON.parse(response); - root.data.contributors = data || []; - root.contributors = root.data.contributors; - console.log("[Github] Contributors fetched from GitHub:", root.contributors.length); + const data = JSON.parse(response) + root.data.contributors = data || [] + root.contributors = root.data.contributors + console.log("[Github] Contributors fetched from GitHub:", root.contributors.length) } else { - console.log("[Github] Empty response from GitHub API for contributors"); - root.data.contributors = []; - root.contributors = []; + console.log("[Github] Empty response from GitHub API for contributors") + root.data.contributors = [] + root.contributors = [] } } catch (e) { - console.error("[Github] Failed to parse contributors:", e); - root.data.contributors = []; - root.contributors = []; + console.error("[Github] Failed to parse contributors:", e) + root.data.contributors = [] + root.contributors = [] } - + // Check if both processes are done - checkAndSaveData(); + checkAndSaveData() } } } @@ -175,8 +174,8 @@ Singleton { function checkAndSaveData() { // Only save when both processes are finished if (!versionProcess.running && !contributorsProcess.running) { - root.isFetchingData = false; - root.saveData(); + root.isFetchingData = false + root.saveData() } } -} \ No newline at end of file +}