Skip to content

Commit b9fc53f

Browse files
committed
Add a button to load older changelogs
1 parent ca9ea3f commit b9fc53f

File tree

3 files changed

+184
-115
lines changed

3 files changed

+184
-115
lines changed

index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
<link rel="image_src" href="https://s2v.app/static/preview.jpg">
1111
<link rel="preload" href="static/Manrope.woff2" as="font" type="font/woff2" crossorigin>
1212
<link rel="preconnect" href="https://api.github.com">
13-
<link rel="stylesheet" href="static/style.css?v=1771968810">
14-
<script src="static/main.js?v=1771968810" defer></script>
13+
<link rel="stylesheet" href="static/style.css?v=1772031956">
14+
<script src="static/main.js?v=1772031956" type="module"></script>
1515
<meta name="description" content="Browse VPK archives, view, extract, and decompile Source 2 assets. Supports CS2, Dota 2, Deadlock, Half-Life: Alyx, and more.">
1616
<meta property="og:description" content="Browse VPK archives, view, extract, and decompile Source 2 assets. Supports CS2, Dota 2, Deadlock, Half-Life: Alyx, and more.">
1717
<meta property="og:url" content="https://s2v.app/">

static/main.js

Lines changed: 157 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,182 @@
1-
fetch('https://api.github.com/repositories/42366054/releases?per_page=5', {
2-
headers: {
3-
Accept: 'application/vnd.github.html+json',
4-
},
5-
})
6-
.then((response) => {
7-
if (!response.ok) {
8-
throw new Error('Failed to fetch github releases');
9-
}
1+
const releasesPerPage = 5;
2+
const releaseNotesContainer = document.getElementById('changelog');
3+
const downloadFormatter = new Intl.NumberFormat('en', {
4+
notation: 'compact',
5+
maximumFractionDigits: 1,
6+
});
7+
let releasesPage = 1;
8+
9+
function LoadReleases() {
10+
const existingBtn = releaseNotesContainer.querySelector(
11+
'.changelog-load-more',
12+
);
1013

11-
return response.json();
12-
})
13-
.then((releases) => {
14-
if (!releases || releases.length === 0) {
15-
return;
16-
}
14+
fetch(
15+
`https://api.github.com/repositories/42366054/releases?per_page=${releasesPerPage}&page=${releasesPage}`,
16+
{
17+
headers: {
18+
Accept: 'application/vnd.github.html+json',
19+
},
20+
},
21+
)
22+
.then((response) => {
23+
if (!response.ok) {
24+
throw new Error('Failed to fetch github releases');
25+
}
26+
27+
return response.json();
28+
})
29+
.then((releases) => {
30+
if (existingBtn) {
31+
existingBtn.remove();
32+
}
1733

18-
const latestRelease = releases[0];
34+
if (!releases || releases.length === 0) {
35+
return;
36+
}
1937

20-
try {
21-
const currentVersion = latestRelease.tag_name;
22-
const storedVersion = localStorage.getItem('s2v-last-version');
38+
const isFirstPage = releasesPage === 1;
39+
releasesPage++;
2340

24-
if (storedVersion && storedVersion !== currentVersion) {
25-
const banner = document.getElementById('js-update-banner');
26-
banner.hidden = false;
41+
if (isFirstPage) {
42+
const latestRelease = releases[0];
2743

28-
banner.addEventListener('click', () => {
29-
banner.hidden = true;
30-
localStorage.setItem('s2v-last-version', currentVersion);
31-
});
32-
} else {
33-
localStorage.setItem('s2v-last-version', currentVersion);
34-
}
35-
} catch (e) {
36-
console.error(e);
37-
}
44+
try {
45+
const currentVersion = latestRelease.tag_name;
46+
const storedVersion = localStorage.getItem('s2v-last-version');
47+
48+
if (storedVersion && storedVersion !== currentVersion) {
49+
const banner = document.getElementById('js-update-banner');
50+
banner.hidden = false;
51+
52+
banner.addEventListener('click', () => {
53+
banner.hidden = true;
54+
localStorage.setItem('s2v-last-version', currentVersion);
55+
});
56+
} else {
57+
localStorage.setItem('s2v-last-version', currentVersion);
58+
}
59+
} catch (e) {
60+
console.error(e);
61+
}
3862

39-
for (const asset of latestRelease.assets) {
40-
if (asset.name === 'Source2Viewer.exe') {
41-
document.getElementById('js-download').href =
42-
asset.browser_download_url;
63+
for (const asset of latestRelease.assets) {
64+
if (asset.name === 'Source2Viewer.exe') {
65+
document.getElementById('js-download').href =
66+
asset.browser_download_url;
4367

44-
const version = document.querySelector('.download-text');
45-
version.textContent = `Download v${latestRelease.tag_name}`;
46-
break;
68+
const version = document.querySelector('.download-text');
69+
version.textContent = `Download v${latestRelease.tag_name}`;
70+
break;
71+
}
72+
}
4773
}
48-
}
4974

50-
const releaseNotesContainer = document.getElementById('changelog');
51-
const downloadFormatter = new Intl.NumberFormat('en', {
52-
notation: 'compact',
53-
maximumFractionDigits: 1,
54-
});
75+
RenderReleases(releases, isFirstPage);
76+
})
77+
.catch((e) => {
78+
console.error(e);
5579

56-
releases.forEach((release, index) => {
57-
const isLatest = index === 0;
58-
59-
const releaseSection = document.createElement('div');
60-
releaseSection.className = 'release-notes-content';
61-
62-
const releaseSidebar = document.createElement('div');
63-
releaseSidebar.className = 'release-notes-sidebar';
64-
65-
const releaseHeader = document.createElement('a');
66-
releaseHeader.className = 'release-version';
67-
releaseHeader.href = release.html_url;
68-
releaseHeader.target = '_blank';
69-
releaseHeader.rel = 'noopener';
70-
releaseHeader.textContent = `v${release.tag_name}`;
71-
releaseSidebar.appendChild(releaseHeader);
72-
73-
const releaseDateSpan = document.createElement('span');
74-
releaseDateSpan.className = 'release-date';
75-
const releaseDate = new Date(release.published_at);
76-
releaseDateSpan.textContent = releaseDate.toLocaleDateString();
77-
releaseSidebar.appendChild(releaseDateSpan);
78-
79-
const totalDownloads = release.assets.reduce(
80-
(sum, asset) => sum + asset.download_count,
81-
0,
82-
);
83-
84-
if (totalDownloads > 0) {
85-
const downloadsSpan = document.createElement('span');
86-
downloadsSpan.className = 'release-downloads';
87-
downloadsSpan.textContent = `${downloadFormatter.format(totalDownloads)} downloads`;
88-
releaseSidebar.appendChild(downloadsSpan);
80+
if (existingBtn) {
81+
existingBtn.disabled = false;
82+
existingBtn.textContent = 'Load older changelogs';
8983
}
84+
});
85+
}
9086

91-
releaseSection.appendChild(releaseSidebar);
87+
function RenderReleases(releases, isFirstPage) {
88+
for (const release of releases) {
89+
const releaseSection = document.createElement('div');
90+
releaseSection.className = 'release-notes-content';
91+
92+
const releaseSidebar = document.createElement('div');
93+
releaseSidebar.className = 'release-notes-sidebar';
94+
95+
const releaseHeader = document.createElement('a');
96+
releaseHeader.className = 'release-version';
97+
releaseHeader.href = release.html_url;
98+
releaseHeader.target = '_blank';
99+
releaseHeader.rel = 'noopener';
100+
releaseHeader.textContent = `v${release.tag_name}`;
101+
releaseSidebar.appendChild(releaseHeader);
102+
103+
const releaseDateSpan = document.createElement('span');
104+
releaseDateSpan.className = 'release-date';
105+
const releaseDate = new Date(release.published_at);
106+
releaseDateSpan.textContent = releaseDate.toLocaleDateString();
107+
releaseSidebar.appendChild(releaseDateSpan);
108+
109+
const totalDownloads = release.assets.reduce(
110+
(sum, asset) => sum + asset.download_count,
111+
0,
112+
);
113+
114+
if (totalDownloads > 0) {
115+
const downloadsSpan = document.createElement('span');
116+
downloadsSpan.className = 'release-downloads';
117+
downloadsSpan.textContent = `${downloadFormatter.format(totalDownloads)} downloads`;
118+
releaseSidebar.appendChild(downloadsSpan);
119+
}
92120

93-
const releaseMain = document.createElement('div');
94-
releaseMain.className = 'release-notes-main';
121+
releaseSection.appendChild(releaseSidebar);
95122

96-
const releaseContent = document.createElement('div');
97-
releaseContent.className = 'release-content';
98-
releaseContent.innerHTML = release.body_html;
99-
AdjustChangelog(releaseContent);
100-
releaseMain.appendChild(releaseContent);
123+
const releaseMain = document.createElement('div');
124+
releaseMain.className = 'release-notes-main';
101125

102-
if (isLatest) {
103-
const releaseAssetsContainer = document.createElement('div');
104-
releaseAssetsContainer.className = 'release-assets';
105-
releaseAssetsContainer.id = 'js-release-assets';
126+
const releaseContent = document.createElement('div');
127+
releaseContent.className = 'release-content';
128+
releaseContent.innerHTML = release.body_html;
129+
AdjustChangelog(releaseContent);
130+
releaseMain.appendChild(releaseContent);
106131

107-
for (const asset of release.assets) {
108-
let name = asset.name;
132+
if (isFirstPage && release === releases[0]) {
133+
const releaseAssetsContainer = document.createElement('div');
134+
releaseAssetsContainer.className = 'release-assets';
135+
releaseAssetsContainer.id = 'js-release-assets';
109136

110-
if (name.endsWith('.zip')) {
111-
name = name.substring(0, name.length - 4).replace(/-/g, ' ');
112-
}
137+
for (const asset of release.assets) {
138+
let name = asset.name;
113139

114-
const assetLink = document.createElement('a');
115-
assetLink.href = asset.browser_download_url;
116-
assetLink.className = 'asset-link';
117-
assetLink.download = '';
118-
assetLink.textContent = name;
119-
releaseAssetsContainer.appendChild(assetLink);
140+
if (name.endsWith('.zip')) {
141+
name = name.substring(0, name.length - 4).replace(/-/g, ' ');
120142
}
121143

122-
const githubLink = document.createElement('a');
123-
githubLink.href = release.html_url;
124-
githubLink.className = 'asset-link';
125-
githubLink.target = '_blank';
126-
githubLink.rel = 'noopener';
127-
githubLink.textContent = 'View release on GitHub';
128-
releaseAssetsContainer.appendChild(githubLink);
129-
130-
releaseMain.appendChild(releaseAssetsContainer);
144+
const assetLink = document.createElement('a');
145+
assetLink.href = asset.browser_download_url;
146+
assetLink.className = 'asset-link';
147+
assetLink.download = '';
148+
assetLink.textContent = name;
149+
releaseAssetsContainer.appendChild(assetLink);
131150
}
132151

133-
releaseSection.appendChild(releaseMain);
152+
const githubLink = document.createElement('a');
153+
githubLink.href = release.html_url;
154+
githubLink.className = 'asset-link';
155+
githubLink.target = '_blank';
156+
githubLink.rel = 'noopener';
157+
githubLink.textContent = 'View release on GitHub';
158+
releaseAssetsContainer.appendChild(githubLink);
159+
160+
releaseMain.appendChild(releaseAssetsContainer);
161+
}
134162

135-
releaseNotesContainer.appendChild(releaseSection);
163+
releaseSection.appendChild(releaseMain);
164+
165+
releaseNotesContainer.appendChild(releaseSection);
166+
}
167+
168+
if (releases.length >= releasesPerPage) {
169+
const loadMoreBtn = document.createElement('button');
170+
loadMoreBtn.className = 'changelog-load-more';
171+
loadMoreBtn.textContent = 'Load older changelogs';
172+
loadMoreBtn.addEventListener('click', () => {
173+
loadMoreBtn.disabled = true;
174+
loadMoreBtn.textContent = 'Loading\u2026';
175+
LoadReleases();
136176
});
137-
});
177+
releaseNotesContainer.appendChild(loadMoreBtn);
178+
}
179+
}
138180

139181
function AdjustChangelog(changelogContainer) {
140182
const wrapMedia = (element, unwrapParent) => {
@@ -254,6 +296,8 @@ function LoadWorkshop() {
254296
});
255297
}
256298

299+
LoadReleases();
300+
257301
if ('IntersectionObserver' in window) {
258302
const observer = new window.IntersectionObserver(
259303
(entries) => {

static/style.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,31 @@ a.hljs-built_in {
10401040
}
10411041
}
10421042

1043+
.changelog-load-more {
1044+
display: block;
1045+
margin: 0 auto 1.5rem;
1046+
padding: 0.6rem 1.5rem;
1047+
background: transparent;
1048+
border: 1px solid rgb(71 168 255 / 20%);
1049+
border-radius: 6px;
1050+
color: rgb(255 255 255 / 70%);
1051+
font: inherit;
1052+
cursor: pointer;
1053+
transition:
1054+
border-color 0.2s,
1055+
color 0.2s;
1056+
1057+
&:hover {
1058+
border-color: var(--color-accent);
1059+
color: var(--color-accent);
1060+
}
1061+
1062+
&:disabled {
1063+
cursor: default;
1064+
opacity: 0.6;
1065+
}
1066+
}
1067+
10431068
@media (max-width: 768px) {
10441069
> h2 {
10451070
font-size: 2rem;

0 commit comments

Comments
 (0)