mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Changelogs: overhaul
This commit is contained in:
@@ -135,54 +135,37 @@ SmartPanel {
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: releaseHighlights
|
||||
delegate: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: Style.marginS
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("changelog.panel.section.version", {
|
||||
"version": modelData.version || I18n.tr("system.unknown-version")
|
||||
})
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
pointSize: Style.fontSizeL
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: modelData.date && modelData.date.length > 0
|
||||
text: I18n.tr("changelog.panel.section.released", {
|
||||
"date": root.formatReleaseDate(modelData.date)
|
||||
})
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeXS
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Repeater {
|
||||
model: releaseHighlights
|
||||
delegate: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: Style.marginXS
|
||||
|
||||
Repeater {
|
||||
model: modelData.entries
|
||||
delegate: NText {
|
||||
readonly property bool isHeading: root.isEmojiHeading(modelData)
|
||||
text: modelData.length === 0 ? "\u00A0" : modelData
|
||||
readonly property int headingLevel: root.headingLevel(modelData)
|
||||
text: {
|
||||
if (modelData.length === 0)
|
||||
return "\u00A0";
|
||||
if (headingLevel > 0)
|
||||
return modelData.replace(/^#+\s+/, "");
|
||||
return modelData;
|
||||
}
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideNone
|
||||
textFormat: Text.PlainText
|
||||
color: isHeading ? Color.mPrimary : Color.mOnSurface
|
||||
font.weight: isHeading ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
pointSize: isHeading ? Style.fontSizeXL : Style.fontSizeM
|
||||
color: headingLevel > 0 ? Color.mPrimary : Color.mOnSurface
|
||||
font.weight: headingLevel > 0 ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
pointSize: headingLevel === 1 ? Style.fontSizeXXL : headingLevel === 2 ? Style.fontSizeXL : Style.fontSizeM
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
visible: index < releaseHighlights.length - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,16 +209,16 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
function isEmojiHeading(text) {
|
||||
function headingLevel(text) {
|
||||
if (!text)
|
||||
return false;
|
||||
return 0;
|
||||
const trimmed = text.trim();
|
||||
if (trimmed.length === 0)
|
||||
return false;
|
||||
if (/^##\s*/i.test(trimmed))
|
||||
return false;
|
||||
const emojiHeading = /^[\u2600-\u27BF\u{1F300}-\u{1FAFF}]\s+/u;
|
||||
return emojiHeading.test(trimmed);
|
||||
return 0;
|
||||
const match = trimmed.match(/^(#+)\s+/);
|
||||
if (!match)
|
||||
return 0;
|
||||
return Math.min(match[1].length, 2);
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
|
||||
@@ -36,6 +36,7 @@ Singleton {
|
||||
|
||||
// Changelog fetching
|
||||
property string changelogBaseUrl: Quickshell.env("NOCTALIA_CHANGELOG_URL") || "https://noctalia.dev:7777/changelogs"
|
||||
property string upgradeLogBaseUrl: Quickshell.env("NOCTALIA_UPGRADELOG_URL") || "https://noctalia.dev:7777/upgradelog"
|
||||
property int changelogFetchLimit: 25
|
||||
property int changelogUpdateFrequency: 60 * 60 // 1 hour in seconds
|
||||
property bool isFetchingChangelogs: false
|
||||
@@ -68,15 +69,6 @@ Singleton {
|
||||
Logger.i("UpdateService", "Version:", root.currentVersion);
|
||||
}
|
||||
|
||||
// Watch for changes to trigger highlight rebuilds
|
||||
onReleasesChanged: {
|
||||
rebuildHighlights();
|
||||
}
|
||||
|
||||
onReleaseNotesChanged: {
|
||||
rebuildHighlights();
|
||||
}
|
||||
|
||||
// Changelog data cache
|
||||
FileView {
|
||||
id: changelogDataFileView
|
||||
@@ -155,68 +147,68 @@ Singleton {
|
||||
|
||||
previousVersion = fromVersion;
|
||||
changelogCurrentVersion = toVersion;
|
||||
releaseHighlights = buildReleaseHighlights(previousVersion, changelogCurrentVersion);
|
||||
releaseNotesUrl = buildReleaseNotesUrl(toVersion);
|
||||
|
||||
// Fetch the upgrade log from the server
|
||||
fetchUpgradeLog(fromVersion, toVersion);
|
||||
|
||||
popupScheduled = true;
|
||||
root.popupQueued(previousVersion, changelogCurrentVersion);
|
||||
|
||||
clearChangelogRequest();
|
||||
openWhenReady();
|
||||
}
|
||||
|
||||
function rebuildHighlights() {
|
||||
if (!changelogCurrentVersion)
|
||||
return;
|
||||
releaseHighlights = buildReleaseHighlights(previousVersion, changelogCurrentVersion);
|
||||
}
|
||||
function fetchUpgradeLog(fromVersion, toVersion) {
|
||||
// Use the last seen version, or default to v3.0.0 if this is a fresh install
|
||||
let from = fromVersion || changelogLastSeenVersion || "v3.0.0";
|
||||
let to = toVersion;
|
||||
|
||||
function buildReleaseHighlights(fromVersion, toVersion) {
|
||||
const selected = [];
|
||||
const fromNorm = normalizeVersion(fromVersion);
|
||||
const toNorm = normalizeVersion(toVersion);
|
||||
// Strip -dev suffix from versions
|
||||
from = from.replace(/-dev$/, "");
|
||||
to = to.replace(/-dev$/, "");
|
||||
|
||||
if (releases.length > 0) {
|
||||
for (var i = 0; i < releases.length; i++) {
|
||||
const rel = releases[i];
|
||||
const tag = rel.version || "";
|
||||
const tagNorm = normalizeVersion(tag);
|
||||
if (!tagNorm)
|
||||
continue;
|
||||
const url = `${upgradeLogBaseUrl}/${from}/${to}`;
|
||||
|
||||
if (toNorm && compareVersions(tagNorm, toNorm) > 0) {
|
||||
continue;
|
||||
Logger.w("UpdateService", "=== Fetching upgrade log ===");
|
||||
Logger.w("UpdateService", "From version:", from);
|
||||
Logger.w("UpdateService", "To version:", to);
|
||||
Logger.w("UpdateService", "URL:", url);
|
||||
Logger.w("UpdateService", "upgradeLogBaseUrl:", upgradeLogBaseUrl);
|
||||
Logger.w("UpdateService", "changelogLastSeenVersion:", changelogLastSeenVersion);
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === XMLHttpRequest.DONE) {
|
||||
Logger.d("UpdateService", "Request completed with status:", request.status);
|
||||
Logger.d("UpdateService", "Response text length:", request.responseText ? request.responseText.length : 0);
|
||||
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
const content = request.responseText || "";
|
||||
Logger.d("UpdateService", "Successfully fetched upgrade log, parsing...");
|
||||
const entries = parseReleaseNotes(content);
|
||||
Logger.d("UpdateService", "Parsed entries count:", entries.length);
|
||||
releaseHighlights = [
|
||||
{
|
||||
"version": toVersion,
|
||||
"date": "",
|
||||
"entries": entries
|
||||
}
|
||||
];
|
||||
fetchError = "";
|
||||
openWhenReady();
|
||||
} else {
|
||||
Logger.e("UpdateService", "Failed to fetch upgrade log");
|
||||
Logger.e("UpdateService", "Status:", request.status);
|
||||
Logger.e("UpdateService", "Status text:", request.statusText);
|
||||
Logger.e("UpdateService", "Response:", request.responseText);
|
||||
fetchError = I18n.tr("changelog.error.fetch-failed");
|
||||
releaseHighlights = [];
|
||||
openWhenReady();
|
||||
}
|
||||
|
||||
if (fromNorm && compareVersions(tagNorm, fromNorm) <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const entries = parseReleaseNotes(rel.body);
|
||||
if (entries.length === 0)
|
||||
continue;
|
||||
|
||||
selected.push({
|
||||
"version": tag,
|
||||
"date": rel.createdAt || "",
|
||||
"entries": entries
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.length === 0 && toVersion) {
|
||||
const fallback = parseReleaseNotes(releaseNotes);
|
||||
if (fallback.length > 0) {
|
||||
selected.push({
|
||||
"version": toVersion,
|
||||
"date": "",
|
||||
"entries": fallback
|
||||
});
|
||||
fetchError = "";
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
};
|
||||
request.open("GET", url);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function normalizeVersion(version) {
|
||||
@@ -265,26 +257,6 @@ Singleton {
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const trimmed = line.trim();
|
||||
|
||||
if (trimmed.match(/^Release\s+v[0-9]/i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.match(/^##\s*Changes since/i)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If this line is just an emoji and the next line has text, merge them
|
||||
if (trimmed.match(/^[\u{1F000}-\u{1F9FF}]$/u) && i + 1 < lines.length) {
|
||||
const nextLine = lines[i + 1].trim();
|
||||
if (nextLine.length > 0) {
|
||||
entries.push(trimmed + " " + nextLine);
|
||||
i++; // Skip the next line since we merged it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
entries.push(line);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user