Skip to content

Commit 4feaf7a

Browse files
committed
implement stacked changelog display and bump version to 1.2.7
1 parent 057ebd2 commit 4feaf7a

File tree

6 files changed

+131
-17
lines changed

6 files changed

+131
-17
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "snowify",
3-
"version": "1.2.6",
3+
"version": "1.2.7",
44
"description": "A Spotify-like desktop music player that streams from free sources",
55
"main": "src/main.js",
66
"scripts": {

src/main.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,6 +2098,36 @@ ipcMain.handle('app:getChangelog', async (_event, version) => {
20982098
}
20992099
});
21002100

2101+
// Fetch recent releases (up to 20) for stacked changelogs
2102+
ipcMain.handle('app:getRecentReleases', async () => {
2103+
try {
2104+
const url = 'https://api.github.com/repos/nyakuoff/Snowify/releases?per_page=20';
2105+
const https = require('https');
2106+
const body = await new Promise((resolve, reject) => {
2107+
const req = https.get(url, { headers: { 'User-Agent': 'Snowify', 'Accept': 'application/vnd.github+json' } }, (res) => {
2108+
let data = '';
2109+
res.on('data', chunk => data += chunk);
2110+
res.on('end', () => {
2111+
if (res.statusCode === 200) resolve(data);
2112+
else reject(new Error(`GitHub API ${res.statusCode}`));
2113+
});
2114+
});
2115+
req.on('error', reject);
2116+
req.setTimeout(10000, () => { req.destroy(); reject(new Error('Timeout')); });
2117+
});
2118+
return JSON.parse(body).map(r => ({
2119+
version: r.tag_name?.replace(/^v/, '') || '',
2120+
name: r.name || r.tag_name || '',
2121+
body: r.body || '',
2122+
date: r.published_at || r.created_at || '',
2123+
url: r.html_url || ''
2124+
}));
2125+
} catch (err) {
2126+
console.error('Failed to fetch releases:', err);
2127+
return [];
2128+
}
2129+
});
2130+
21012131
ipcMain.handle('updater:check', async () => {
21022132
if (_isDev) {
21032133
sendUpdateStatus('error', { message: 'Auto-update is not available in dev mode' });

src/preload.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ contextBridge.exposeInMainWorld('snowify', {
8181
// Auto-updater
8282
getVersion: () => ipcRenderer.invoke('app:getVersion'),
8383
getChangelog: (version) => ipcRenderer.invoke('app:getChangelog', version),
84+
getRecentReleases: () => ipcRenderer.invoke('app:getRecentReleases'),
8485
checkForUpdates: () => ipcRenderer.invoke('updater:check'),
8586
installUpdate: () => ipcRenderer.send('updater:install'),
8687
onUpdateStatus: (callback) => {

src/renderer/app.js

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6090,7 +6090,24 @@
60906090
return html;
60916091
}
60926092

6093-
async function openChangelog(version) {
6093+
/** Compare two semver strings. Returns -1, 0, or 1. */
6094+
function compareSemver(a, b) {
6095+
const pa = a.split('.').map(Number);
6096+
const pb = b.split('.').map(Number);
6097+
for (let i = 0; i < 3; i++) {
6098+
const va = pa[i] || 0, vb = pb[i] || 0;
6099+
if (va < vb) return -1;
6100+
if (va > vb) return 1;
6101+
}
6102+
return 0;
6103+
}
6104+
6105+
/**
6106+
* Open the changelog modal.
6107+
* @param {string} version - The current/target version
6108+
* @param {string} [sinceVersion] - If provided, show all releases after this version up to `version`
6109+
*/
6110+
async function openChangelog(version, sinceVersion) {
60946111
const modal = $('#changelog-modal');
60956112
const body = $('#changelog-body');
60966113
const meta = $('#changelog-meta');
@@ -6101,20 +6118,68 @@
61016118
title.textContent = "What's New";
61026119
modal.classList.remove('hidden');
61036120

6104-
const data = await window.snowify.getChangelog(version);
6121+
// Multi-version mode: fetch releases between sinceVersion and version
6122+
if (sinceVersion && compareSemver(sinceVersion, version) < 0) {
6123+
const releases = await window.snowify.getRecentReleases();
6124+
// Filter to versions > sinceVersion and <= version, sort descending (newest first)
6125+
const missed = releases
6126+
.filter(r => r.version && compareSemver(r.version, sinceVersion) > 0 && compareSemver(r.version, version) <= 0)
6127+
.sort((a, b) => compareSemver(b.version, a.version));
6128+
6129+
if (missed.length === 0) {
6130+
// Fallback to single version
6131+
return openChangelog(version);
6132+
}
61056133

6106-
if (!data || !data.body) {
6107-
body.innerHTML = '<div class="changelog-empty"><p>No changelog available for this version.</p></div>';
6108-
meta.textContent = `v${version}`;
6109-
return;
6110-
}
6134+
title.textContent = missed.length === 1
6135+
? (missed[0].name || `What's New in v${version}`)
6136+
: "What's New";
6137+
meta.textContent = missed.length > 1
6138+
? `${missed.length} updates since v${sinceVersion}`
6139+
: '';
6140+
6141+
let html = '';
6142+
missed.forEach((rel, i) => {
6143+
if (missed.length > 1) {
6144+
const dateStr = rel.date
6145+
? new Date(rel.date).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
6146+
: '';
6147+
html += `<div class="changelog-version-section${i > 0 ? ' changelog-version-divider' : ''}">`;
6148+
html += `<h2 class="changelog-version-heading">${escapeHtml(rel.name || `v${rel.version}`)}</h2>`;
6149+
if (dateStr) html += `<p class="changelog-version-date">${dateStr}</p>`;
6150+
}
6151+
html += renderMarkdown(rel.body || '');
6152+
if (missed.length > 1) html += '</div>';
6153+
});
6154+
6155+
body.innerHTML = html;
61116156

6112-
title.textContent = data.name || `What's New in v${data.version}`;
6113-
if (data.date) {
6114-
const d = new Date(data.date);
6115-
meta.textContent = `Released ${d.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}`;
6157+
// Single version — set proper title/meta
6158+
if (missed.length === 1) {
6159+
const rel = missed[0];
6160+
title.textContent = rel.name || `What's New in v${rel.version}`;
6161+
if (rel.date) {
6162+
const d = new Date(rel.date);
6163+
meta.textContent = `Released ${d.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}`;
6164+
}
6165+
}
6166+
} else {
6167+
// Single version mode (direct open from button)
6168+
const data = await window.snowify.getChangelog(version);
6169+
6170+
if (!data || !data.body) {
6171+
body.innerHTML = '<div class="changelog-empty"><p>No changelog available for this version.</p></div>';
6172+
meta.textContent = `v${version}`;
6173+
return;
6174+
}
6175+
6176+
title.textContent = data.name || `What's New in v${data.version}`;
6177+
if (data.date) {
6178+
const d = new Date(data.date);
6179+
meta.textContent = `Released ${d.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}`;
6180+
}
6181+
body.innerHTML = renderMarkdown(data.body);
61166182
}
6117-
body.innerHTML = renderMarkdown(data.body);
61186183

61196184
// Make links open externally
61206185
body.querySelectorAll('a[href]').forEach(a => {
@@ -6149,8 +6214,8 @@
61496214
const version = await window.snowify.getVersion();
61506215
const lastSeenVersion = localStorage.getItem('snowify_last_changelog_version');
61516216
if (lastSeenVersion && lastSeenVersion !== version) {
6152-
// Version changed — show changelog after a short delay
6153-
setTimeout(() => openChangelog(version), 1500);
6217+
// Version changed — show stacked changelog for all missed versions
6218+
setTimeout(() => openChangelog(version, lastSeenVersion), 1500);
61546219
}
61556220
localStorage.setItem('snowify_last_changelog_version', version);
61566221
})();

src/renderer/styles.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3176,6 +3176,24 @@ html, body {
31763176
color: var(--text-secondary);
31773177
}
31783178

3179+
/* Stacked changelog version sections */
3180+
.changelog-version-divider {
3181+
margin-top: 24px;
3182+
padding-top: 24px;
3183+
border-top: 1px solid var(--border);
3184+
}
3185+
.changelog-version-heading {
3186+
font-size: 1.15em;
3187+
font-weight: 700;
3188+
color: var(--text-primary);
3189+
margin: 0 0 2px 0;
3190+
}
3191+
.changelog-version-date {
3192+
font-size: 0.82em;
3193+
color: var(--text-secondary);
3194+
margin: 0 0 12px 0;
3195+
}
3196+
31793197
/* ═══════ Input Modal ═══════ */
31803198
.modal-overlay {
31813199
position: fixed;

0 commit comments

Comments
 (0)