diff --git a/src/app/Main.tsx b/src/app/Main.tsx index 8c55eff6..c7070ac8 100644 --- a/src/app/Main.tsx +++ b/src/app/Main.tsx @@ -1,7 +1,7 @@ import { render } from 'preact' import type { RouterOnChangeArgs } from 'preact-router' -import { Router } from 'preact-router' -import { useEffect, useState } from 'preact/hooks' +import { getCurrentUrl, Router } from 'preact-router' +import { useCallback, useEffect, useState } from 'preact/hooks' import config from '../config.json' import '../styles/global.css' import '../styles/nodes.css' @@ -10,8 +10,9 @@ import { Header } from './components' import { loadLocale, locale, Locales } from './Locales' import { Category, Changelog, Generator, Home, Sounds } from './pages' import type { VersionId } from './services' +import { VersionIds } from './services' import { Store } from './Store' -import { cleanUrl } from './Utils' +import { cleanUrl, getSearchParams, setSeachParams } from './Utils' const VERSIONS_IN_TITLE = 3 @@ -46,18 +47,28 @@ function Main() { document.documentElement.setAttribute('data-theme', theme) }, [theme]) + const searchParams = getSearchParams(getCurrentUrl()) + const targetVersion = searchParams.get('version') const [version, setVersion] = useState(Store.getVersion()) - const changeVersion = (version: VersionId) => { + const changeVersion = useCallback((version: VersionId) => { + if (getSearchParams(getCurrentUrl()).has('version')) { + setSeachParams({ version }) + } Analytics.setVersion(version) Store.setVersion(version) setVersion(version) - } + }, [targetVersion]) + useEffect(() => { + if (VersionIds.includes(targetVersion as VersionId) && version !== targetVersion) { + setVersion(targetVersion as VersionId) + } + }, [version, targetVersion]) const [title, setTitle] = useState(locale(lang, 'title.home')) const changeTitle = (title: string, versions?: VersionId[]) => { versions ??= config.versions.map(v => v.id as VersionId) - versions.splice(0, versions.length - VERSIONS_IN_TITLE) - document.title = `${title} Minecraft ${versions.join(', ')}` + const titleVersions = versions.slice(versions.length - VERSIONS_IN_TITLE) + document.title = `${title} Minecraft ${titleVersions.join(', ')}` setTitle(title) } diff --git a/src/app/Utils.ts b/src/app/Utils.ts index 323adbda..aeca880f 100644 --- a/src/app/Utils.ts +++ b/src/app/Utils.ts @@ -1,5 +1,6 @@ import type { DataModel } from '@mcschema/core' import { Path } from '@mcschema/core' +import { getCurrentUrl, route } from 'preact-router' import rfdc from 'rfdc' import config from '../config.json' @@ -53,11 +54,44 @@ export function cleanUrl(url: string) { return `/${url}/`.replaceAll('//', '/') } +export function getPath(url: string) { + const searchIndex = url.indexOf('?') + if (searchIndex >= 0) { + url = url.slice(0, searchIndex) + } + return cleanUrl(url) +} + export function getGenerator(url: string) { - const trimmedUrl = url.replace(/^\//, '').replace(/\/$/, '').replace(/\?.*/, '') + const trimmedUrl = getPath(url).replace(/^\//, '').replace(/\/$/, '') return config.generators.find(g => g.url === trimmedUrl) } +export function getSearchParams(url: string) { + const searchIndex = url.indexOf('?') + if (searchIndex >= 0) { + url = url.slice(searchIndex + 1) + return new Map(url.split('&').map<[string, string]>(param => { + const index = param.indexOf('=') + if (index === -1) return [param, 'true'] + return [decodeURIComponent(param.slice(0, index)), decodeURIComponent(param.slice(index + 1))] + })) + } + return new Map() +} + +export function setSeachParams(modifications: Record) { + const url = getCurrentUrl() + const searchParams = getSearchParams(url) + Object.entries(modifications).forEach(([key, value]) => { + if (value === undefined) searchParams.delete(key) + else searchParams.set(key, value) + }) + const search = Array.from(searchParams).map(([key, value]) => + `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) + route(`${getPath(url)}${search.length === 0 ? '' : `?${search.join('&')}`}`, true) +} + export function stringToColor(str: string): [number, number, number] { const h = Math.abs(hashString(str)) return [h % 256, (h >> 8) % 256, (h >> 16) % 256] diff --git a/src/app/pages/Generator.tsx b/src/app/pages/Generator.tsx index 9fddc54e..f08dc985 100644 --- a/src/app/pages/Generator.tsx +++ b/src/app/pages/Generator.tsx @@ -8,7 +8,7 @@ import { useModel } from '../hooks' import { locale } from '../Locales' import type { BlockStateRegistry, VersionId } from '../services' import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel } from '../services' -import { getGenerator, message } from '../Utils' +import { getGenerator, getSearchParams, message, setSeachParams } from '../Utils' type GeneratorProps = { lang: string, @@ -40,6 +40,9 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat setError(`The minimum version for this generator is ${gen.minVersion}`) } + const searchParams = getSearchParams(getCurrentUrl()) + const currentPreset = searchParams.get('preset') + const [model, setModel] = useState(null) const [blockStates, setBlockStates] = useState(null) useEffect(() => { @@ -48,14 +51,19 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat getBlockStates(version) .then(b => setBlockStates(b)) getModel(version, gen.id) - .then(m => { + .then(async m => { Analytics.setGenerator(gen.id) + if (currentPreset) { + const preset = await loadPreset(currentPreset) + m.reset(DataModel.wrapLists(preset), false) + } setModel(m) }) .catch(e => { console.error(e); setError(message(e)) }) }, [version, gen.id]) useModel(model, () => { + setSeachParams({ version: undefined, preset: undefined }) setError(null) }) @@ -106,9 +114,18 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat .catch(e => { console.error(e); setError(e.message) }) }, [version, gen.id, presetFilter]) - const loadPreset = (id: string) => { + const selectPreset = (id: string) => { + loadPreset(id).then(preset => { + model?.reset(DataModel.wrapLists(preset), false) + setSeachParams({ version, preset: id }) + }) + } + + const loadPreset = async (id: string) => { Analytics.generatorEvent('load-preset', id) - fetchPreset(version, gen.path ?? gen.id, id).then(preset => { + console.log('load preset', version, gen.id, id) + try { + const preset = await fetchPreset(version, gen.path ?? gen.id, id) const seed = model?.get(new Path(['generator', 'seed'])) if (preset?.generator?.seed !== undefined && seed !== undefined) { preset.generator.seed = seed @@ -116,8 +133,10 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat preset.generator.biome_source.seed = seed } } - model?.reset(DataModel.wrapLists(preset), false) - }) + return preset + } catch (e) { + setError(message(e)) + } } const [sourceShown, setSourceShown] = useState(window.innerWidth > 820) @@ -176,7 +195,7 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat
- {presetResults.map(preset => loadPreset(preset)} />)} + {presetResults.map(preset => selectPreset(preset)} />)}
{presetResults.length === 0 && }
diff --git a/src/app/services/DataFetcher.ts b/src/app/services/DataFetcher.ts index 3ca6c6ee..6a4b1dc2 100644 --- a/src/app/services/DataFetcher.ts +++ b/src/app/services/DataFetcher.ts @@ -213,7 +213,7 @@ export async function fetchPreset(version: VersionId, registry: string, id: stri } return await res.json() } catch (e) { - console.warn(`Error occurred while fetching ${registry} preset ${id}:`, message(e)) + throw new Error(`Error occurred while fetching ${registry} preset ${id}: ${message(e)}`) } } diff --git a/src/config.json b/src/config.json index 3e41169b..96ddbe06 100644 --- a/src/config.json +++ b/src/config.json @@ -91,8 +91,7 @@ "id": "loot_table", "url": "loot-table", "path": "loot_tables", - "schema": "loot_table", - "maxVersion": "1.17" + "schema": "loot_table" }, { "id": "predicate",