mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 15:47:08 +00:00
Improve homepage (#245)
* Improve how generators are listed on home * Add some icons for generators * Remove debug * Refactor cachedFetch and use generated changelogs * Add limit to how many changes are shown by default * Add more generator icons * Refactor cards * Fix generator icons for light theme * Add more worldgen icons * Add remaining generator icons * Refactor navigation and badges style * Group on homepage for guides and tools * Fix header button style * Add versions and technical changelog to homepage * Make it clear that not all changes could be documented
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { marked } from 'marked'
|
||||
import type { Change } from '../../services/index.js'
|
||||
import { ChangelogTag } from './index.js'
|
||||
import { Card } from '../Card.jsx'
|
||||
import { Badge } from './index.js'
|
||||
|
||||
type Props = {
|
||||
change: Change,
|
||||
@@ -8,14 +9,13 @@ type Props = {
|
||||
toggleTag?: (tag: string) => unknown,
|
||||
}
|
||||
export function ChangelogEntry({ change, activeTags, toggleTag }: Props) {
|
||||
return <div class="changelog-entry">
|
||||
<div class="changelog-version">
|
||||
<a href={`/versions/?id=${change.version}`}>{change.version}</a>
|
||||
<a href={`/versions/?id=${change.group}`}>{change.group}</a>
|
||||
</div>
|
||||
<div class="changelog-tags">
|
||||
{change.tags.map(tag => <ChangelogTag label={tag} onClick={toggleTag ? () => toggleTag(tag) : undefined} active={activeTags?.includes(tag)} />)}
|
||||
return <Card overlay={<>
|
||||
<a class="changelog-version" href={`/versions/?id=${change.version}`}>{change.version}</a>
|
||||
<a class="changelog-version" href={`/versions/?id=${change.group}`}>{change.group}</a>
|
||||
</>}>
|
||||
<div class="badges-list">
|
||||
{change.tags.map(tag => <Badge label={tag} onClick={toggleTag ? () => toggleTag(tag) : undefined} active={activeTags?.includes(tag)} />)}
|
||||
</div>
|
||||
<div class="changelog-content" dangerouslySetInnerHTML={{ __html: marked(change.content) }} />
|
||||
</div>
|
||||
</Card>
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import type { ComponentChildren } from 'preact'
|
||||
import { useMemo, useState } from 'preact/hooks'
|
||||
import { useLocale } from '../../contexts/index.js'
|
||||
import { useSearchParam, useTags } from '../../hooks/index.js'
|
||||
import type { Change } from '../../services/index.js'
|
||||
import { Badge } from '../Badge.jsx'
|
||||
import { Btn, TextInput } from '../index.js'
|
||||
import { ChangelogEntry } from './ChangelogEntry.js'
|
||||
import { ChangelogTag } from './ChangelogTag.js'
|
||||
|
||||
const SEARCH_KEY = 'search'
|
||||
|
||||
interface Props {
|
||||
changes: Change[] | undefined,
|
||||
defaultOrder: 'asc' | 'desc',
|
||||
limit?: number,
|
||||
navigation?: ComponentChildren,
|
||||
}
|
||||
export function ChangelogList({ changes, defaultOrder }: Props) {
|
||||
export function ChangelogList({ changes, defaultOrder, limit, navigation }: Props) {
|
||||
const { locale } = useLocale()
|
||||
|
||||
const [search, setSearch] = useSearchParam(SEARCH_KEY)
|
||||
@@ -25,7 +28,7 @@ export function ChangelogList({ changes, defaultOrder }: Props) {
|
||||
if (!tags.every(tag => change.tags.includes(tag))) {
|
||||
return false
|
||||
}
|
||||
const content = change.tags.join(' ') + ' ' + change.content.toLowerCase()
|
||||
const content = `${change.group} ${change.version} ${change.tags.join(' ')} ${change.content.toLowerCase()}`
|
||||
return query.every(q => {
|
||||
if (q.startsWith('!')) {
|
||||
return q.length === 1 || !content.includes(q.slice(1))
|
||||
@@ -41,22 +44,35 @@ export function ChangelogList({ changes, defaultOrder }: Props) {
|
||||
return filteredChangelogs?.sort((a, b) => sort ? b.order - a.order : a.order - b.order)
|
||||
}, [filteredChangelogs, sort])
|
||||
|
||||
const [limitActive, setLimitActive] = useState(true)
|
||||
|
||||
const limitedChangelogs = useMemo(() => {
|
||||
if (!limitActive || (limit ?? -1) < 0) return sortedChangelogs
|
||||
return sortedChangelogs?.slice(0, limit)
|
||||
}, [sortedChangelogs, limitActive, limit, sort /* why is this necessary??? */])
|
||||
|
||||
const hiddenChanges = (sortedChangelogs?.length ?? 0) - (limitedChangelogs?.length ?? 0)
|
||||
|
||||
return <>
|
||||
<div class="changelog-query">
|
||||
<TextInput class="btn btn-input changelog-search" list="sound-list" placeholder={locale('changelog.search')}
|
||||
<div class="navigation">
|
||||
{navigation}
|
||||
<TextInput class="btn btn-input query-search" list="sound-list" placeholder={locale('changelog.search')}
|
||||
value={search} onChange={v => setSearch(v, true)} />
|
||||
<Btn icon={sort ? 'sort_desc' : 'sort_asc'} label={sort ? 'Newest first' : 'Oldest first'} onClick={() => setSort(!sort)} />
|
||||
</div>
|
||||
{tags.length > 0 && <div class="changelog-tags">
|
||||
{tags.map(tag => <ChangelogTag label={tag} onClick={() => toggleTag(tag)} />)}
|
||||
{tags.length > 0 && <div class="badges-list">
|
||||
{tags.map(tag => <Badge label={tag} onClick={() => toggleTag(tag)} />)}
|
||||
</div>}
|
||||
<div class="changelog-list">
|
||||
{sortedChangelogs === undefined
|
||||
<div class="result-list">
|
||||
{limitedChangelogs === undefined
|
||||
? <span class="note">{locale('loading')}</span>
|
||||
: sortedChangelogs.length === 0
|
||||
: limitedChangelogs.length === 0
|
||||
? <span class="note">{locale('changelog.no_results')}</span>
|
||||
: sortedChangelogs.map(change =>
|
||||
: limitedChangelogs.map(change =>
|
||||
<ChangelogEntry change={change} activeTags={tags} toggleTag={toggleTag} />)}
|
||||
{hiddenChanges > 0 && (
|
||||
<Btn label={locale('changelog.show_more', `${hiddenChanges}`)} onClick={() => setLimitActive(false)}/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { hashString } from '../../Utils.js'
|
||||
import { Octicon } from '../index.js'
|
||||
|
||||
type TagProps = {
|
||||
label: string,
|
||||
active?: boolean,
|
||||
onClick?: (e: MouseEvent) => unknown,
|
||||
}
|
||||
export function ChangelogTag({ label, active, onClick }: TagProps) {
|
||||
const color = label === 'breaking' ? 5 : hashString(label) % 360
|
||||
return <div class={`changelog-tag${active ? ' active' : ''}${onClick ? ' clickable' : ''}`} style={`--tint: ${color}`} onClick={onClick}>
|
||||
{label === 'breaking' && Octicon.alert}
|
||||
{label}
|
||||
</div>
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from 'preact/hooks'
|
||||
import { useMemo, useState } from 'preact/hooks'
|
||||
import { useLocale } from '../../contexts/index.js'
|
||||
import type { Change, VersionMeta } from '../../services/index.js'
|
||||
import { getArticleLink, getChangelogs } from '../../services/index.js'
|
||||
import { useAsync } from '../../hooks/useAsync.js'
|
||||
import type { VersionMeta } from '../../services/index.js'
|
||||
import { fetchChangelogs, getArticleLink } from '../../services/index.js'
|
||||
import { Giscus } from '../Giscus.js'
|
||||
import { Octicon } from '../Octicon.js'
|
||||
import { ChangelogList } from './ChangelogList.js'
|
||||
@@ -18,18 +19,11 @@ export function VersionDetail({ id, version }: Props) {
|
||||
|
||||
const [tab, setTab] = useState<Tab>('changelog')
|
||||
|
||||
const [changelogs, setChangelogs] = useState<Change[] | undefined>(undefined)
|
||||
useEffect(() => {
|
||||
getChangelogs()
|
||||
.then(changelogs => setChangelogs(
|
||||
changelogs.map(c => ({ ...c, tags: c.tags.filter(t => t !== c.group) }))
|
||||
))
|
||||
.catch(e => console.error(e))
|
||||
}, [])
|
||||
const { value: changes } = useAsync(fetchChangelogs, [])
|
||||
|
||||
const filteredChangelogs = useMemo(() =>
|
||||
changelogs?.filter(c => c.version === id || c.group === id),
|
||||
[id, changelogs])
|
||||
changes?.filter(c => c.version === id || c.group === id),
|
||||
[id, changes])
|
||||
|
||||
const articleLink = version && getArticleLink(version.id)
|
||||
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { useMemo, useState } from 'preact/hooks'
|
||||
import type { ComponentChildren } from 'preact'
|
||||
import { useMemo } from 'preact/hooks'
|
||||
import { useLocale } from '../../contexts/index.js'
|
||||
import { useSearchParam } from '../../hooks/index.js'
|
||||
import { useLocalStorage, useSearchParam } from '../../hooks/index.js'
|
||||
import type { VersionMeta } from '../../services/index.js'
|
||||
import { Checkbox, TextInput } from '../index.js'
|
||||
import { VersionEntry } from './VersionEntry.js'
|
||||
|
||||
const INCLUDE_SNAPSHOTS = 'misode_include_snapshots'
|
||||
const SEARCH_KEY = 'search'
|
||||
|
||||
interface Props {
|
||||
versions: VersionMeta[]
|
||||
link?: (id: string) => string
|
||||
versions?: VersionMeta[],
|
||||
link?: (id: string) => string,
|
||||
navigation?: ComponentChildren,
|
||||
}
|
||||
export function VersionList({ versions, link }: Props) {
|
||||
export function VersionList({ versions, link, navigation }: Props) {
|
||||
const { locale } = useLocale()
|
||||
|
||||
const [snapshots, setSnapshots] = useState(true)
|
||||
const [snapshots, setSnapshots] = useLocalStorage(INCLUDE_SNAPSHOTS, true, v => v === 'true', b => `${b}`)
|
||||
const [search, setSearch] = useSearchParam(SEARCH_KEY)
|
||||
|
||||
const filteredVersions = useMemo(() => versions.filter(v => {
|
||||
const filteredVersions = useMemo(() => versions?.filter(v => {
|
||||
if (v.type === 'snapshot' && !snapshots) return false
|
||||
return v.id.includes(search ?? '')
|
||||
}), [versions, snapshots, search])
|
||||
|
||||
|
||||
return <>
|
||||
<div class="versions-controls">
|
||||
<TextInput class="btn btn-input version-search" placeholder={locale('versions.search')}
|
||||
<div class="navigation">
|
||||
{navigation}
|
||||
<TextInput class="btn btn-input query-search" placeholder={locale('versions.search')}
|
||||
value={search} onChange={setSearch} />
|
||||
<Checkbox label="Include snapshots" value={snapshots} onChange={setSnapshots} />
|
||||
</div>
|
||||
<div class="version-list">
|
||||
{filteredVersions.map(v => <VersionEntry version={v} link={link?.(v.id)} />)}
|
||||
{filteredVersions.length === 0 && <span>
|
||||
{locale('versions.no_results')}
|
||||
</span>}
|
||||
{filteredVersions === undefined
|
||||
? <span class="note">{locale('loading')}</span>
|
||||
: filteredVersions.length === 0
|
||||
? <span class="note">{locale('versions.no_results')}</span>
|
||||
: filteredVersions.map(v => <VersionEntry version={v} link={link?.(v.id)} />)}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from '../Badge.jsx'
|
||||
export * from './ChangelogEntry.js'
|
||||
export * from './ChangelogList.js'
|
||||
export * from './ChangelogTag.js'
|
||||
export * from './VersionDetail.js'
|
||||
export * from './VersionEntry.js'
|
||||
export * from './VersionList.js'
|
||||
|
||||
Reference in New Issue
Block a user