Support multiple versions in technical changelog

- Better handling of article links
- Style improvements
This commit is contained in:
Misode
2021-11-02 02:25:04 +01:00
parent 582d5f4b1e
commit cc20cb7f7a
3 changed files with 70 additions and 17 deletions

View File

@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from 'preact/hooks'
import { Ad, ErrorPanel, Octicon, TextInput } from '../components' import { Ad, ErrorPanel, Octicon, TextInput } from '../components'
import { locale } from '../Locales' import { locale } from '../Locales'
import type { VersionId } from '../Schemas' import type { VersionId } from '../Schemas'
import type { ChangelogEntry } from '../services/Changelogs' import type { ChangelogEntry, ChangelogVersion } from '../services/Changelogs'
import { getChangelogs } from '../services/Changelogs' import { getChangelogs } from '../services/Changelogs'
import { hashString } from '../Utils' import { hashString } from '../Utils'
@@ -26,9 +26,11 @@ export function Changelog({ lang, changeTitle }: ChangelogProps) {
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
const [tags, setTags] = useState<string[]>([]) const [tags, setTags] = useState<string[]>([])
const addTag = (tag: string) => { const toggleTag = (tag: string) => {
if (!tags.includes(tag)) { if (!tags.includes(tag)) {
setTags([...tags, tag]) setTags([...tags, tag])
} else {
setTags(tags.filter(t => t !== tag))
} }
} }
@@ -56,7 +58,7 @@ export function Changelog({ lang, changeTitle }: ChangelogProps) {
</div> </div>
<div class="changelog"> <div class="changelog">
{filteredChangelogs.map(change => {filteredChangelogs.map(change =>
<Change change={change} activeTags={tags} addTag={addTag} />)} <Change change={change} activeTags={tags} toggleTag={toggleTag} />)}
</div> </div>
</main> </main>
} }
@@ -64,18 +66,27 @@ export function Changelog({ lang, changeTitle }: ChangelogProps) {
type ChangeProps = { type ChangeProps = {
change: ChangelogEntry, change: ChangelogEntry,
activeTags: string[], activeTags: string[],
addTag: (tag: string) => unknown, toggleTag: (tag: string) => unknown,
} }
function Change({ change, activeTags, addTag }: ChangeProps) { function Change({ change, activeTags, toggleTag }: ChangeProps) {
return <div class="changelog-entry"> return <div class="changelog-entry">
<div class="changelog-version">
<ArticleLink {...change.version}/>
<ArticleLink {...change.group}/>
</div>
<div class="changelog-tags"> <div class="changelog-tags">
{change.tags.map(tag => <Tag label={tag} onClick={() => addTag(tag)} active={activeTags.includes(tag)} />)} {change.tags.map(tag => <Tag label={tag} onClick={() => toggleTag(tag)} active={activeTags.includes(tag)} />)}
<a class="changelog-version" href={`https://www.minecraft.net/en-us/article/minecraft-snapshot-${change.version}`}>{change.version}</a>
</div> </div>
<div class="changelog-content" dangerouslySetInnerHTML={{ __html: marked(change.content) }} /> <div class="changelog-content" dangerouslySetInnerHTML={{ __html: marked(change.content) }} />
</div> </div>
} }
function ArticleLink({ id, article }: ChangelogVersion) {
return article === null
? <span>{id}</span>
: <a href={`https://www.minecraft.net/en-us/article/${article}`} target="_blank">{id}</a>
}
type TagProps = { type TagProps = {
label: string, label: string,
active?: boolean, active?: boolean,

View File

@@ -1,33 +1,40 @@
import { isObject } from '../Utils'
const repo = 'https://raw.githubusercontent.com/misode/technical-changes/main' const repo = 'https://raw.githubusercontent.com/misode/technical-changes/main'
export type ChangelogEntry = { export type ChangelogEntry = {
group: string, group: ChangelogVersion,
version: string, version: ChangelogVersion,
tags: string[], tags: string[],
content: string, content: string,
} }
export type ChangelogVersion = {
id: string,
article: string | null,
}
let Changelogs: ChangelogEntry[] | Promise<ChangelogEntry[]> | null = null let Changelogs: ChangelogEntry[] | Promise<ChangelogEntry[]> | null = null
export async function getChangelogs() { export async function getChangelogs() {
if (!Changelogs) { if (!Changelogs) {
const index = await (await fetch(`${repo}/index.json`)).json() as string[] const index = await (await fetch(`${repo}/index.json`)).json() as string[]
Changelogs = (await Promise.all( Changelogs = (await Promise.all(
index.map(group => fetchGroup(group)) index.map(group => fetchGroup(parseVersion(group)))
)).flat() )).flat()
} }
return Changelogs return Changelogs
} }
async function fetchGroup(group: string) { async function fetchGroup(group: ChangelogVersion) {
const index = await (await fetch(`${repo}/${group}/index.json`)).json() as string[] const index = await (await fetch(`${repo}/${group.id}/index.json`)).json() as string[]
return (await Promise.all( return (await Promise.all(
index.map(version => fetchChangelog(group, version)) index.map(version => fetchChangelog(group, parseVersion(version)))
)).flat() )).flat()
} }
async function fetchChangelog(group: string, version: string) { async function fetchChangelog(group: ChangelogVersion, version: ChangelogVersion) {
const text = await (await fetch(`${repo}/${group}/${version}.md`)).text() const text = await (await fetch(`${repo}/${group.id}/${version.id}.md`)).text()
return parseChangelog(text).map(change => ({ return parseChangelog(text).map(change => ({
version, version,
group, group,
@@ -47,3 +54,15 @@ function parseChangelog(text: string) {
} }
}) })
} }
function parseVersion(version: unknown): ChangelogVersion {
if (typeof version === 'string') {
return {
id: version,
article: version.match(/\d\dw\d\d[a-z]/) ? 'minecraft-snapshot-' + version : version.match(/\d+\.\d+(\.\d+)?-pre[0-9]+/) ? 'minecraft-' + version.replaceAll('.', '-').replaceAll('pre', 'pre-release-') : null,
}
} else if (isObject(version)) {
return version as ChangelogVersion
}
return { id: 'unknown', article: null }
}

View File

@@ -1004,7 +1004,7 @@ hr {
.changelog-tags { .changelog-tags {
display: flex; display: flex;
margin-bottom: 8px; flex-wrap: wrap;
} }
.changelog-tag { .changelog-tag {
@@ -1018,12 +1018,17 @@ hr {
padding: 0 8px; padding: 0 8px;
color: var(--color); color: var(--color);
fill: var(--color); fill: var(--color);
white-space: nowrap;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
} }
.changelog-entry .changelog-tag {
margin-bottom: 8px;
}
.changelog-tag svg { .changelog-tag svg {
margin-right: 4px; margin-right: 4px;
width: 20px; width: 20px;
@@ -1041,13 +1046,31 @@ hr {
} }
.changelog-version { .changelog-version {
float: right;
}
.changelog-version > * {
margin-left: auto; margin-left: auto;
font-size: 15px; font-size: 15px;
color: var(--text-3); color: var(--text-3);
text-decoration: none; text-decoration: none;
} }
.changelog-version:hover { .changelog-version > *:first-child {
position: relative;
margin-right: 19px;
}
.changelog-version > *:first-child::after {
content: '•';
position: absolute;
text-decoration: none;
right: -12px;
top: 0;
pointer-events: none;
}
.changelog-version a:hover {
text-decoration: underline; text-decoration: underline;
} }