diff --git a/CHANGELOG.md b/CHANGELOG.md index 81fb01bc..9094f21e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -255,6 +255,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Fixed +### Settings — contributors list sorted chronologically + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#700](https://github.com/Psychotoxical/psysonic/pull/700)** + +* The **Settings → System → Contributors** list rendered in raw insertion order, so the original maintainer (since v1.0.0) showed up last and the hand-maintained ordering drifted as new entries were appended. It is now sorted on render — ascending by the app version a contributor first appeared in, tie-broken by their first-contribution PR number — so it stays correct no matter where new entries land in the source list. + ### Internet Radio — Add / Edit station modal no longer clipped on empty library **By [@cucadmuh](https://github.com/cucadmuh), thanks to voidboywannabe for the report on the Psysonic Discord, PR [#699](https://github.com/Psychotoxical/psysonic/pull/699)** diff --git a/src/config/settingsCredits.test.ts b/src/config/settingsCredits.test.ts new file mode 100644 index 00000000..cf88b5d0 --- /dev/null +++ b/src/config/settingsCredits.test.ts @@ -0,0 +1,24 @@ +import { describe, it, expect } from 'vitest'; +import { CONTRIBUTORS } from './settingsCredits'; +import { isNewer } from '../utils/componentHelpers/appUpdaterHelpers'; + +describe('CONTRIBUTORS ordering', () => { + it('is sorted ascending by the `since` app version', () => { + for (let i = 1; i < CONTRIBUTORS.length; i++) { + // a preceding entry must never be newer than the one after it + expect(isNewer(CONTRIBUTORS[i - 1].since, CONTRIBUTORS[i].since)).toBe(false); + } + }); + + it('puts the original maintainer (v1.0.0) first', () => { + expect(CONTRIBUTORS[0].github).toBe('Psychotoxical'); + }); + + it('breaks `since` ties by first-contribution PR number', () => { + // nullobject (PR #7) and trbn1 (PR #9) both first appeared in v1.22.0 + const nullobject = CONTRIBUTORS.findIndex(c => c.github === 'nullobject'); + const trbn1 = CONTRIBUTORS.findIndex(c => c.github === 'trbn1'); + expect(nullobject).toBeGreaterThanOrEqual(0); + expect(nullobject).toBeLessThan(trbn1); + }); +}); diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index d4824051..89f052f9 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -1,5 +1,7 @@ +import { isNewer } from '../utils/componentHelpers/appUpdaterHelpers'; + // Credits list rendered on the Settings → System tab. Update via PR when adding a contributor. -export const CONTRIBUTORS = [ +const CONTRIBUTOR_ENTRIES = [ { github: 'jiezhuo', since: '1.21', @@ -284,6 +286,22 @@ export const CONTRIBUTORS = [ }, ] as const; +// PR number of a contributor's first listed contribution, used as the +// tie-breaker when several contributors share the same `since` version. +function firstContributionPr(entry: { contributions: readonly string[] }): number { + const match = entry.contributions[0]?.match(/PR #(\d+)/); + return match ? Number(match[1]) : Number.MAX_SAFE_INTEGER; +} + +// Rendered chronologically: ascending by the app version a contributor first +// appeared in (`since`), then by their first-contribution PR number. Sorting +// here keeps the order correct no matter where new entries land in the array. +export const CONTRIBUTORS = [...CONTRIBUTOR_ENTRIES].sort((a, b) => { + if (isNewer(a.since, b.since)) return 1; + if (isNewer(b.since, a.since)) return -1; + return firstContributionPr(a) - firstContributionPr(b); +}); + export const MAINTAINERS = [ { github: 'Psychotoxical' }, { github: 'cucadmuh' },