feat(directory): proper cache handling

This commit is contained in:
Haoyu Xu
2023-02-28 17:34:37 -05:00
parent e4648945d1
commit 3cc0f48648
7 changed files with 95 additions and 34 deletions

View File

@@ -1,4 +1,2 @@
VITE_APP_TITLE=AKLive2D
VITE_VERSION=0.5.9
VITE_APP_VOICE_URL=jp/CN_037.ogg
VITE_SHOWCASE_VERSION=3.3.13
VITE_APP_VOICE_URL=jp/CN_037.ogg

View File

@@ -1 +1 @@
0.5.9
0.5.12

View File

@@ -1,29 +1,91 @@
import { createContext, useState, useEffect } from "react"
import db from "@/db"
import { createContext, useState, useEffect, useCallback } from "react"
import db, { invalidateCache } from "@/db"
const versionCompare = (v1, v2) => {
const v1Arr = v1.split(".")
const v2Arr = v2.split(".")
for (let i = 0; i < v1Arr.length; i++) {
if (v1Arr[i] > v2Arr[i]) {
return 1
} else if (v1Arr[i] < v2Arr[i]) {
return -1
}
}
return 0
}
const invalidateRules = (local, version) => {
if (local === undefined) {
// no local version
return true
}
if (version === undefined) {
// no remote version
return false
}
return versionCompare(local, version) < 0
}
export const ConfigContext = createContext()
export function ConfigProvider(props) {
const [config, setConfig] = useState([])
const [operators, setOperators] = useState([])
const [version, setVersion] = useState({})
useEffect(() => {
const fetchConfig = (version) => {
fetch("/_assets/directory.json").then(res => res.json()).then(data => {
setConfig(data)
db.config.put({ key: "config", value: data })
let operatorsList = []
data.operators.forEach((item) => {
operatorsList = [...operatorsList, ...item]
})
setOperators(operatorsList)
resolveOperators(data)
db.config.put({ key: "version", value: version })
})
}
const resolveOperators = useCallback((data) => {
let operatorsList = []
data.operators.forEach((item) => {
operatorsList = [...operatorsList, ...item]
})
setOperators(operatorsList)
}, [])
useEffect(() => {
fetch("/_assets/version.json").then(res => res.json()).then(data => {
db.config.put({ key: "version", value: data })
db.config.get({ key: "version" }).then((local) => {
if (local === undefined || invalidateRules(local.value.directory, data.directory) || invalidateRules(local.value.showcase, data.showcase)) {
invalidateCache()
fetchConfig(data)
} else {
db.config.get({ key: "config" }).then((local) => {
if (local) {
setConfig(local.value)
resolveOperators(local.value)
} else {
fetchConfig(data)
}
})
}
})
setVersion(data)
})
/*
local.directory | version.directory | action
--------------- | ----------------- | ------
1.0.0 | 1.0.0 | use cache
1.0.0 | 1.0.1 | invalidate cache
1.0.1 | 1.0.0 | impossible
local.showcase | version.showcase | action
-------------- | ---------------- | ------
1.0.0 | 1.0.0 | use cache
1.0.0 | 1.0.1 | invalidate cache
1.0.1 | 1.0.0 | use cache
*/
}, [])
return (
<ConfigContext.Provider value={{ config, operators }}>
<ConfigContext.Provider value={{ config, operators, version }}>
{props.children}
</ConfigContext.Provider>
)

View File

@@ -7,4 +7,10 @@ db.version(2).stores({
config: '++key, value',
});
export function invalidateCache() {
db.image.clear();
db.voice.clear();
db.config.clear();
}
export default db;

View File

@@ -87,15 +87,15 @@ export default function Home() {
const loadVoice = (link) => {
if (!voiceOn) return
db.voice.get({ key: link }).then((v) => {
if (v === undefined) {
if (v) {
playVoice(v.blob)
} else {
fetch(`/${link}/assets/voice/${import.meta.env.VITE_APP_VOICE_URL}`)
.then(res => res.blob())
.then(blob => {
db.voice.put({ key: link, blob: blob })
playVoice(blob)
})
} else {
playVoice(v.blob)
}
})
}
@@ -162,15 +162,15 @@ function ImageElement({ item, language }) {
useEffect(() => {
db.image.get({ key: item.link }).then((v) => {
if (v === undefined) {
if (v) {
setBlobUrl(URL.createObjectURL(v.blob))
} else {
fetch(`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`)
.then(res => res.blob())
.then(blob => {
db.image.put({ key: item.link, blob: blob })
setBlobUrl(URL.createObjectURL(blob))
})
} else {
setBlobUrl(URL.createObjectURL(v.blob))
}
})
}, [item.link])

View File

@@ -12,6 +12,7 @@ import {
} from "react-router-dom";
import './root.css'
import routes from '@/routes'
import { ConfigContext } from '@/context/useConfigContext';
import { HeaderContext } from '@/context/useHeaderContext';
import { LanguageContext } from '@/context/useLanguageContext';
import Dropdown from '@/component/dropdown';
@@ -34,6 +35,7 @@ export default function Root(props) {
currentTab, setCurrentTab,
appbarExtraArea
} = useContext(HeaderContext)
const { version } = useContext(ConfigContext)
const [drawerDestinations, setDrawerDestinations] = useState(null)
const currentYear = new Date().getFullYear()
const [headerTabs, setHeaderTabs] = useState(null)
@@ -197,15 +199,11 @@ export default function Root(props) {
<span>Spine Runtimes © 2013 - 2019 Esoteric Software LLC</span>
<span>Assets © 2017 - {currentYear} Arknights/Hypergryph Co., Ltd</span>
<span>Source Code © 2021 - {currentYear} Halyul</span>
<span>Version: {import.meta.env.VITE_VERSION}</span>
<span>Directory @ {version.directory}</span>
<span>Showcase @ {version.showcase}</span>
</section>
</footer>
<ScrollRestoration
getKey={(location, matches) => {
// default behavior
return location.pathname;
}}
/>
<ScrollRestoration />
</>
)
}

View File

@@ -7,6 +7,7 @@ import getConfig from './libs/config.js'
import { rmdir, writeSync } from './libs/file.js'
import { increase } from './libs/version.js'
import EnvGenerator from './libs/env_generator.js'
import directory from './libs/directory.js'
global.__projetRoot = path.dirname(fileURLToPath(import.meta.url))
@@ -134,21 +135,17 @@ class ViteRunner {
get #directoryConfig() {
if (process.env.npm_lifecycle_event === 'vite:directory:build') {
this.#globalConfig.version.directory = increase(path.join(__projetRoot, "directory"))
global.__config = this.#globalConfig
directory()
}
const directoryDir = path.resolve(__projetRoot, 'directory')
writeSync((new EnvGenerator()).generate([
{
key: "app_title",
value: this.#globalConfig.directory.title
}, {
key: "version",
value: this.#globalConfig.version.directory
}, {
}, {
key: "app_voice_url",
value: this.#globalConfig.directory.voice
}, {
key: "showcase_version",
value: this.#globalConfig.version.showcase
}
]), path.join(directoryDir, '.env'))
this.#mode = process.argv[3]