Add Technical Changelog page

This commit is contained in:
Misode
2021-10-31 18:22:10 +01:00
parent bb1223df60
commit 765f96372f
11 changed files with 268 additions and 3 deletions
-1
View File
@@ -43,7 +43,6 @@ export namespace Analytics {
export function setGenerator(generator: string) {
dimension(DIM_GENERATOR, generator)
console.log(generator)
}
export function generatorEvent(action: string, label?: string) {
+2 -1
View File
@@ -8,7 +8,7 @@ import '../styles/nodes.css'
import { Analytics } from './Analytics'
import { Header } from './components'
import { loadLocale, locale, Locales } from './Locales'
import { Generator, Home, Sounds, Worldgen } from './pages'
import { Changelog, Generator, Home, Sounds, Worldgen } from './pages'
import type { VersionId } from './Schemas'
import { Store } from './Store'
import { cleanUrl } from './Utils'
@@ -72,6 +72,7 @@ function Main() {
<Home path="/" {...{lang, changeTitle}} />
<Worldgen path="/worldgen" {...{lang, changeTitle}} />
<Sounds path="/sounds" {...{lang, version, changeTitle, changeVersion}} />
<Changelog path="/changelog" {...{lang, changeTitle}} />
<Generator default {...{lang, version, changeTitle, changeVersion}} />
</Router>
</>
+87
View File
@@ -0,0 +1,87 @@
import marked from 'marked'
import { useEffect, useMemo, useState } from 'preact/hooks'
import { Ad, ErrorPanel, TextInput } from '../components'
import { locale } from '../Locales'
import type { VersionId } from '../Schemas'
import type { ChangelogEntry } from '../services/Changelogs'
import { getChangelogs } from '../services/Changelogs'
import { hashString } from '../Utils'
type ChangelogProps = {
path?: string,
lang: string,
changeTitle: (title: string, versions?: VersionId[]) => unknown,
}
export function Changelog({ lang, changeTitle }: ChangelogProps) {
const loc = locale.bind(null, lang)
const [error, setError] = useState<string | null>(null)
changeTitle(loc('title.changelog'))
const [changelogs, setChangelogs] = useState<ChangelogEntry[]>([])
useEffect(() => {
getChangelogs()
.then(changelogs => setChangelogs(changelogs))
.catch(e => { console.error(e); setError(e) })
}, [])
const [search, setSearch] = useState('')
const [tags, setTags] = useState<string[]>([])
const addTag = (tag: string) => {
if (!tags.includes(tag)) {
setTags([...tags, tag])
}
}
const filteredChangelogs = useMemo(() => {
const query = search.split(' ').map(q => q.trim().toLowerCase()).filter(q => q.length > 0)
if (query.length === 0 && tags.length === 0) return changelogs
return changelogs.filter(change => {
if (!tags.every(tag => change.tags.includes(tag))) {
return false
}
const content = change.tags.join(' ') + ' ' + change.content.toLowerCase()
return query.every(q => content.includes(q))
})
}, [changelogs, search, tags])
return <main>
<Ad type="text" id="changelog" />
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
<div class="changelog-controls">
<TextInput class="btn btn-input changelog-search" list="sound-list" placeholder={loc('changelog.search')}
value={search} onChange={setSearch} />
{tags.length > 0 && <div class="changelog-tags">
{tags.map(tag => <Tag label={tag} onClick={() => setTags(tags.filter(t => t !== tag))} />)}
</div>}
</div>
<div class="changelog">
{filteredChangelogs.map(change =>
<Change change={change} activeTags={tags} addTag={addTag} />)}
</div>
</main>
}
type ChangeProps = {
change: ChangelogEntry,
activeTags: string[],
addTag: (tag: string) => unknown,
}
function Change({ change, activeTags, addTag }: ChangeProps) {
return <div class="changelog-entry">
<div class="changelog-tags">
{change.tags.map(tag => <Tag label={tag} onClick={() => addTag(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 dangerouslySetInnerHTML={{ __html: marked(change.content) }} />
</div>
}
type TagProps = {
label: string,
active?: boolean,
onClick?: () => unknown,
}
function Tag({ label, active, onClick }: TagProps) {
const color = hashString(label) % 360
return <div class={`changelog-tag${active ? ' active' : ''}${onClick ? ' clickable' : ''}`} style={`--tint: ${color}`} onClick={onClick}>{label}</div>
}
+1
View File
@@ -27,6 +27,7 @@ export function Home({ lang, changeTitle }: HomeProps) {
<ToolCard title="Data Pack Upgrader" link="https://misode.github.io/upgrader/">
<p>Convert your 1.16 data packs to 1.17</p>
</ToolCard>
<ToolCard title="Technical Changelog" link="/changelog/" />
</div>
</main>
}
+1
View File
@@ -1,3 +1,4 @@
export * from './Changelog'
export * from './Generator'
export * from './Home'
export * from './Sounds'
+47
View File
@@ -0,0 +1,47 @@
const repo = 'https://raw.githubusercontent.com/misode/technical-changes/main'
export type ChangelogEntry = {
group: string,
version: string,
tags: string[],
content: string,
}
let Changelogs: ChangelogEntry[] | Promise<ChangelogEntry[]> | null = null
export async function getChangelogs() {
if (!Changelogs) {
const index = await (await fetch(`${repo}/index.json`)).json() as string[]
Changelogs = (await Promise.all(
index.map(group => fetchGroup(group))
)).flat()
}
return Changelogs
}
async function fetchGroup(group: string) {
const index = await (await fetch(`${repo}/${group}/index.json`)).json() as string[]
return (await Promise.all(
index.map(version => fetchChangelog(group, version))
)).flat()
}
async function fetchChangelog(group: string, version: string) {
const text = await (await fetch(`${repo}/${group}/${version}.md`)).text()
return parseChangelog(text).map(change => ({
version,
group,
...change,
}))
}
function parseChangelog(text: string) {
return text.split('\n\n')
.map(entry => {
const i = entry.indexOf('|')
return {
tags: entry.substring(0, i).trim().split(' '),
content: entry.slice(i + 1).trim(),
}
})
}
+2
View File
@@ -3,6 +3,7 @@
"add_bottom": "Add to bottom",
"add_top": "Add to top",
"advancement": "Advancement",
"changelog.search": "Search changes",
"collapse": "Collapse",
"collapse_all": "Hold %0% to collapse all",
"configure_layers": "Configure layers",
@@ -63,6 +64,7 @@
"theme.dark": "Dark",
"theme.light": "Light",
"theme.system": "System",
"title.changelog": "Technical Changelog",
"title.generator": "%0% Generator",
"title.generator_category": "%0% Generators",
"title.home": "Data Pack Generators",
+91 -1
View File
@@ -22,6 +22,8 @@
--errors-background: #62190f;
--errors-text: #ffffffcc;
--invalid-text: #fd7951;
--text-saturation: 60%;
--text-lightness: 45%;
}
:root[data-theme=light] {
@@ -48,6 +50,8 @@
--errors-background: #f66653;
--errors-text: #000000cc;
--invalid-text: #a32600;
--text-saturation: 100%;
--text-lightness: 30%;
}
@media (prefers-color-scheme: light) {
@@ -75,6 +79,8 @@
--errors-background: #f66653;
--errors-text: #000000cc;
--invalid-text: #a32600;
--text-saturation: 100%;
--text-lightness: 35%;
}
}
@@ -379,11 +385,18 @@ main.has-preview {
display: flex;
flex-direction: column;
position: absolute;
right: 0;
top: 100%;
margin-top: 8px;
}
.btn-menu.menu-sw .btn-group {
right: 0;
}
.btn-menu.menu-se .btn-group {
left: 0;
}
.btn-group {
border-radius: 6px;
box-shadow: 0 0 7px -2px #000;
@@ -975,6 +988,83 @@ hr {
color: var(--invalid-text);
}
.changelog {
display: flex;
flex-direction: column;
padding: 16px;
}
.changelog-entry {
background: var(--background-2);
border-radius: 4px;
margin-bottom: 8px;
padding: 8px;
color: var(--text-2);
}
.changelog-tags {
display: flex;
margin-bottom: 8px;
}
.changelog-tag {
--color: hsl(var(--tint, 0), var(--text-saturation), var(--text-lightness));
margin-right: 8px;
border: 1.5px solid var(--color);
height: 24px;
border-radius: 12px;
padding: 0 8px;
color: var(--color);
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.changelog-tag.clickable {
cursor: pointer;
}
.changelog-tag.active {
background-color: var(--color);
color: var(--background-2);
}
.changelog-version {
margin-left: auto;
font-size: 15px;
color: var(--text-3);
text-decoration: none;
}
.changelog-version:hover {
text-decoration: underline;
}
.changelog-entry code {
background-color: var(--background-5);
padding: 1px 4px;
border-radius: 4px;
color: var(--text-1);
}
.changelog-controls {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.changelog-search {
flex-basis: 100%;
padding: 8px;
background-color: var(--background-2);
border-radius: 6px;
}
.changelog-controls .changelog-tags {
margin: 8px 0 0;
}
@media screen and (max-width: 720px) {
.sound-search-group {
margin-bottom: 8px;