Skip to content

Commit 559cdb4

Browse files
committed
feat: improve changelogs page
- by default only show releases notes of installed version - menu to select and read `latest`, `beta`, etc - linkify the mentions and pr number similar to github
1 parent 807b45e commit 559cdb4

File tree

2 files changed

+199
-25
lines changed

2 files changed

+199
-25
lines changed

src/pages/changelog/changelog.js

Lines changed: 165 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,182 @@
1+
import fsOperation from "fileSystem";
12
import "./style.scss";
3+
import Contextmenu from "components/contextmenu";
24
import Page from "components/page";
5+
import Ref from "html-tag-js/ref";
36
import actionStack from "lib/actionStack";
47
import markdownIt from "markdown-it";
58
import markdownItTaskLists from "markdown-it-task-lists";
69
import helpers from "utils/helpers";
710

811
export default async function Changelog() {
9-
const CHANGELOG_URL =
10-
"https://raw.githubusercontent.com/deadlyjack/Acode/main/CHANGELOG.md";
11-
const $page = Page(strings["changelog"]);
12-
const $content = <div className="md" id="changelog"></div>;
12+
const GITHUB_API_URL =
13+
"https://api.github.com/repos/Acode-Foundation/Acode/releases";
14+
const CHANGELOG_FILE_URL =
15+
"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/CHANGELOG.md";
16+
const currentVersion = BuildInfo.version;
1317

14-
$content.innerHTML = '<div class="loading">Loading changelog...</div>';
18+
let selectedVersion = currentVersion;
19+
let selectedStatus = "current";
20+
const versionIndicatorRef = new Ref();
21+
const versionTextRef = new Ref();
1522

16-
$page.content = $content;
17-
app.append($page);
23+
const versionSelector = (
24+
<div className="changelog-version-selector" data-action="select-version">
25+
<span
26+
className={"status-indicator status-" + selectedStatus}
27+
ref={versionIndicatorRef}
28+
></span>
29+
<span ref={versionTextRef}>{selectedVersion}</span>
30+
</div>
31+
);
1832

19-
try {
20-
const changeLog = await fetch(CHANGELOG_URL);
21-
const changeLogText = await changeLog.text();
33+
const $page = Page(strings["changelog"], {
34+
tail: versionSelector,
35+
});
2236

23-
const cleanedText = changeLogText.replace(/^#\s*Change\s*Log\s*\n*/i, "");
37+
const versionSelectorMenu = Contextmenu({
38+
top: "36px",
39+
right: "5px",
40+
toggler: versionSelector,
41+
transformOrigin: "top right",
42+
innerHTML: () => {
43+
return `
44+
<li action="current">
45+
<span class="text">Current Version (${currentVersion})</span>
46+
</li>
47+
<li action="latest">
48+
<span class="text">Latest Release</span>
49+
</li>
50+
<li action="beta">
51+
<span class="text">Beta Version</span>
52+
</li>
53+
<li action="full">
54+
<span class="text">Full Changelog</span>
55+
</li>
56+
`;
57+
},
58+
});
2459

25-
const htmlContent = markdownIt({ html: true })
26-
.use(markdownItTaskLists)
27-
.render(cleanedText);
60+
const $content = <div className="md" id="changelog"></div>;
61+
$content.innerHTML = '<div class="loading">Loading changelog...</div>';
62+
$page.content = $content;
63+
app.append($page);
2864

29-
$content.innerHTML = htmlContent;
30-
} catch (error) {
31-
$content.innerHTML = '<div class="error">Failed to load changelog</div>';
32-
}
65+
async function loadLatestRelease() {
66+
try {
67+
const releases = await fsOperation(`${GITHUB_API_URL}/latest`).readFile(
68+
"json",
69+
);
70+
selectedVersion = releases.tag_name.replace("v", "");
71+
selectedStatus = "latest";
72+
updateVersionSelector();
73+
return renderChangelog(releases.body);
74+
} catch (error) {
75+
$content.innerHTML =
76+
'<div class="error">Failed to load latest release notes</div>';
77+
}
78+
}
3379

34-
$page.onhide = function () {
35-
actionStack.remove("changelog");
36-
};
80+
async function loadBetaRelease() {
81+
try {
82+
const releases = await fsOperation(GITHUB_API_URL).readFile("json");
83+
const betaRelease = releases.find((r) => r.prerelease);
84+
selectedVersion = betaRelease.tag_name.replace("v", "");
85+
selectedStatus = "prerelease";
86+
updateVersionSelector();
87+
return renderChangelog(betaRelease.body);
88+
} catch (error) {
89+
$content.innerHTML =
90+
'<div class="error">Failed to load beta release notes</div>';
91+
}
92+
}
3793

38-
actionStack.push({
39-
id: "changelog",
40-
action: $page.hide,
41-
});
94+
async function loadFullChangelog() {
95+
try {
96+
const changeLogText =
97+
await fsOperation(CHANGELOG_FILE_URL).readFile("utf8");
98+
const cleanedText = changeLogText.replace(/^#\s*Change\s*Log\s*\n*/i, "");
99+
selectedVersion = "Changelogs.md";
100+
selectedStatus = "current";
101+
updateVersionSelector();
102+
return renderChangelog(cleanedText);
103+
} catch (error) {
104+
$content.innerHTML =
105+
'<div class="error">Failed to load full changelog</div>';
106+
}
107+
}
108+
109+
async function loadVersionChangelog() {
110+
try {
111+
const releases = await fsOperation(GITHUB_API_URL).readFile("json");
112+
const currentRelease = releases.find(
113+
(r) => r.tag_name.replace("v", "") === currentVersion,
114+
);
115+
selectedVersion = currentVersion;
116+
selectedStatus = "current";
117+
updateVersionSelector();
118+
if (currentRelease) {
119+
return renderChangelog(currentRelease.body);
120+
} else {
121+
return loadLatestRelease();
122+
}
123+
} catch (error) {
124+
$content.innerHTML =
125+
'<div class="error">Failed to load version changelog</div>';
126+
}
127+
}
128+
129+
function renderChangelog(text) {
130+
const md = markdownIt({ html: true, linkify: true });
131+
const REPO_URL = "https://github.com/Acode-Foundation/Acode";
132+
let processedText = text
133+
// Convert full PR URLs to #number format with links preserved in markdown
134+
.replace(/https:\/\/github\.com\/Acode-Foundation\/Acode\/pull\/(\d+)/g, '[#$1](https://github.com/Acode-Foundation/Acode/pull/$1)')
135+
// Convert existing #number references to links if they aren't already
136+
.replace(/(?<!\[)#(\d+)(?!\])/g, '[#$1](https://github.com/Acode-Foundation/Acode/pull/$1)')
137+
// Convert @username mentions to GitHub profile links
138+
.replace(/@(\w+)/g, '[@$1](https://github.com/$1)');
139+
140+
md.use(markdownItTaskLists);
141+
const htmlContent = md.render(processedText);
142+
$content.innerHTML = htmlContent;
143+
}
144+
145+
function updateVersionSelector() {
146+
versionTextRef.textContent = selectedVersion;
147+
versionIndicatorRef.className = "status-indicator status-" + selectedStatus;
148+
}
149+
150+
versionSelectorMenu.onclick = async function(e) {
151+
const action = e.target.closest("li")?.getAttribute("action");
152+
if (!action) return;
153+
versionSelectorMenu.hide();
154+
155+
switch (action) {
156+
case "current":
157+
await loadVersionChangelog();
158+
break;
159+
case "latest":
160+
await loadLatestRelease();
161+
break;
162+
case "beta":
163+
await loadBetaRelease();
164+
break;
165+
case "full":
166+
await loadFullChangelog();
167+
break;
168+
}
169+
};
170+
171+
// Load current version changelog by default
172+
loadVersionChangelog();
173+
174+
$page.onhide = function() {
175+
actionStack.remove("changelog");
176+
};
177+
178+
actionStack.push({
179+
id: "changelog",
180+
action: $page.hide,
181+
});
42182
}

src/pages/changelog/style.scss

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,40 @@
55
padding: 0 1rem;
66
}
77

8+
.changelog-version-selector {
9+
display: flex;
10+
align-items: center;
11+
gap: 8px;
12+
background-color: var(--popup-background-color);
13+
border: none;
14+
border-radius: 8px;
15+
padding: 8px 16px;
16+
font-weight: 500;
17+
cursor: pointer;
18+
transition: all 0.2s ease;
19+
margin-right: 6px;
20+
21+
&:hover {
22+
background-color: var(--secondary-color);
23+
}
24+
}
25+
26+
.status-indicator {
27+
display: inline-block;
28+
width: 8px;
29+
height: 8px;
30+
border-radius: 50%;
31+
}
32+
.status-latest {
33+
background-color: #10b981;
34+
}
35+
.status-prerelease {
36+
background-color: var(--danger-color);
37+
}
38+
.status-current {
39+
background-color: var(--active-icon-color);
40+
}
41+
842
.loading {
943
display: flex;
1044
justify-content: center;

0 commit comments

Comments
 (0)