1
0
forked from a/lifeto-shop
This commit is contained in:
a 2025-06-20 01:18:37 -05:00
parent bd20e23b15
commit f00708e80d
No known key found for this signature in database
GPG Key ID: 2F22877AA4DFDADB
19 changed files with 165 additions and 218 deletions

View File

@ -1,4 +1,5 @@
dist dist
dist/**
**/vendor/** **/vendor/**
**/locales/** **/locales/**
generated.* generated.*

View File

@ -1,7 +1,8 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json", "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"files": { "files": {
"ignoreUnknown": true "ignoreUnknown": true,
"includes": ["src/**/*.{ts,tsx,js,jsx}"]
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,

View File

@ -48,7 +48,8 @@ export const CharacterCard = ({ character }: { character: TricksterCharacter })
return ( return (
<> <>
<div <button
type="button"
onClick={() => { onClick={() => {
setSelectedCharacter(character) setSelectedCharacter(character)
}} }}
@ -64,7 +65,11 @@ export const CharacterCard = ({ character }: { character: TricksterCharacter })
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex flex-row justify-center"> <div className="flex flex-row justify-center">
{character.base_job === -8 ? ( {character.base_job === -8 ? (
<img className="h-8" src="https://beta.lifeto.co/item_img/gel.nri.003.000.png" /> <img
className="h-8"
src="https://beta.lifeto.co/item_img/gel.nri.003.000.png"
alt="Gel character"
/>
) : ( ) : (
<img <img
className="h-16" className="h-16"
@ -75,6 +80,7 @@ export const CharacterCard = ({ character }: { character: TricksterCharacter })
) )
.toString() .toString()
.padStart(3, '0')}_13.png`} .padStart(3, '0')}_13.png`}
alt={`Character ${character.name}`}
/> />
)} )}
</div> </div>
@ -94,7 +100,7 @@ export const CharacterCard = ({ character }: { character: TricksterCharacter })
</FloatingPortal> </FloatingPortal>
</div> </div>
</div> </div>
</div> </button>
</> </>
) )
} }
@ -157,7 +163,7 @@ export const CharacterRoulette = () => {
}} }}
></input> ></input>
<div className="flex flex-row flex-wrap overflow-x-scroll gap-1 h-full min-h-36 max-w-48"> <div className="flex flex-row flex-wrap overflow-x-scroll gap-1 h-full min-h-36 max-w-48">
{searchResults ? searchResults : <></>} {searchResults ? searchResults : null}
</div> </div>
</div> </div>
</> </>

View File

@ -46,7 +46,8 @@ const InventoryTabs = () => {
<div className="flex flex-row gap-1"> <div className="flex flex-row gap-1">
{sections.map(x => { {sections.map(x => {
return ( return (
<div <button
type="button"
onClick={() => { onClick={() => {
setInventoryFilterTab(x.value) setInventoryFilterTab(x.value)
}} }}
@ -55,14 +56,15 @@ const InventoryTabs = () => {
${inventoryFilter.tab === x.value ? selectedStyle : ''}`} ${inventoryFilter.tab === x.value ? selectedStyle : ''}`}
> >
{x.name} {x.name}
</div> </button>
) )
})} })}
</div> </div>
<div className="flex flex-row gap-1"> <div className="flex flex-row gap-1">
{cardSections.map(x => { {cardSections.map(x => {
return ( return (
<div <button
type="button"
onClick={() => { onClick={() => {
setInventoryFilterTab(x.value) setInventoryFilterTab(x.value)
}} }}
@ -71,7 +73,7 @@ ${inventoryFilter.tab === x.value ? selectedStyle : ''}`}
${inventoryFilter.tab === x.value ? selectedStyle : ''}`} ${inventoryFilter.tab === x.value ? selectedStyle : ''}`}
> >
{x.name} {x.name}
</div> </button>
) )
})} })}
</div> </div>
@ -103,41 +105,45 @@ export const Inventory = () => {
<div className="flex flex-col py-2 flex-0 justify-between h-full"> <div className="flex flex-col py-2 flex-0 justify-between h-full">
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
<div <button
type="button"
className="whitespace-pre bg-blue-200 px-2 py-1 rounded-xl hover:cursor-pointer hover:bg-blue-300" className="whitespace-pre bg-blue-200 px-2 py-1 rounded-xl hover:cursor-pointer hover:bg-blue-300"
onClick={() => { onClick={() => {
addPageItemSelection() addPageItemSelection()
}} }}
> >
select filtered select filtered
</div> </button>
<div <button
type="button"
className="whitespace-pre bg-blue-200 px-2 py-1 rounded-xl hover:cursor-pointer hover:bg-blue-300" className="whitespace-pre bg-blue-200 px-2 py-1 rounded-xl hover:cursor-pointer hover:bg-blue-300"
onClick={() => { onClick={() => {
addFilterItemSelection() addFilterItemSelection()
}} }}
> >
select page select page
</div> </button>
<div <button
type="button"
className="whitespace-pre bg-blue-200 px-2 py-1 rounded-xl hover:cursor-pointer hover:bg-blue-300" className="whitespace-pre bg-blue-200 px-2 py-1 rounded-xl hover:cursor-pointer hover:bg-blue-300"
onClick={() => { onClick={() => {
clearItemSelection() clearItemSelection()
}} }}
> >
clear{' '} clear{' '}
</div> </button>
</div> </div>
<div className="flex flex-row"> <div className="flex flex-row">
<InventoryTargetSelector /> <InventoryTargetSelector />
<div <button
type="button"
onClick={_e => { onClick={_e => {
// sendOrders() // sendOrders()
}} }}
className="hover:cursor-pointer whitespace-preborder border-black-1 bg-orange-200 hover:bg-orange-300 px-2 py-1" className="hover:cursor-pointer whitespace-preborder border-black-1 bg-orange-200 hover:bg-orange-300 px-2 py-1"
> >
Move Selected Move Selected
</div> </button>
</div> </div>
</div> </div>
@ -152,22 +158,26 @@ export const Inventory = () => {
setSearch(e.target.value) setSearch(e.target.value)
}} }}
/> />
<div <button
type="button"
className="hover:cursor-pointer border border-black-1 bg-green-200 hover:bg-green-300 px-2 py-1 h-full flex items-center" className="hover:cursor-pointer border border-black-1 bg-green-200 hover:bg-green-300 px-2 py-1 h-full flex items-center"
onClick={() => { onClick={() => {
paginateInventory(-1) paginateInventory(-1)
}} }}
aria-label="Previous page"
> >
<FaArrowLeft /> <FaArrowLeft />
</div> </button>
<div <button
type="button"
className="hover:cursor-pointer border border-black-1 bg-green-200 hover:bg-green-300 px-2 py-1 h-full flex items-center" className="hover:cursor-pointer border border-black-1 bg-green-200 hover:bg-green-300 px-2 py-1 h-full flex items-center"
onClick={() => { onClick={() => {
paginateInventory(1) paginateInventory(1)
}} }}
aria-label="Next page"
> >
<FaArrowRight /> <FaArrowRight />
</div> </button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -28,9 +28,11 @@ const AccountInventorySelectorItem = forwardRef<
return ( return (
<div <div
ref={ref} ref={ref}
// biome-ignore lint/a11y/useSemanticElements: Custom autocomplete component needs role="option"
role="option" role="option"
id={id} id={id}
aria-selected={active} aria-selected={active}
tabIndex={-1}
{...rest} {...rest}
style={{ style={{
background: active ? 'lightblue' : 'none', background: active ? 'lightblue' : 'none',
@ -149,8 +151,8 @@ export const InventoryTargetSelector = () => {
> >
{items.map((item, index) => ( {items.map((item, index) => (
<AccountInventorySelectorItem <AccountInventorySelectorItem
key={item.path}
{...getItemProps({ {...getItemProps({
key: item.path,
ref(node) { ref(node) {
listRef.current[index] = node listRef.current[index] = node
}, },

View File

@ -1,7 +1,7 @@
import { useAtom } from 'jotai' import { useAtom } from 'jotai'
import { useState } from 'react' import { useState } from 'react'
import useLocalStorage from 'use-local-storage' import useLocalStorage from 'use-local-storage'
import { LoginHelper } from '../lib/session' import { login, logout } from '../lib/session'
import { loginStatusAtom } from '../state/atoms' import { loginStatusAtom } from '../state/atoms'
export const LoginWidget = () => { export const LoginWidget = () => {
@ -19,8 +19,9 @@ export const LoginWidget = () => {
<div>{loginState.community_name}</div> <div>{loginState.community_name}</div>
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
<button <button
type="button"
onClick={() => { onClick={() => {
LoginHelper.logout().finally(() => { logout().finally(() => {
refetchLoginState() refetchLoginState()
}) })
return return
@ -40,7 +41,7 @@ export const LoginWidget = () => {
<div className="flex flex-col"> <div className="flex flex-col">
<form <form
action={() => { action={() => {
LoginHelper.login(username, password) login(username, password)
.catch(e => { .catch(e => {
setLoginError(e.message) setLoginError(e.message)
}) })
@ -58,7 +59,6 @@ export const LoginWidget = () => {
setUsername(e.target.value) setUsername(e.target.value)
}} }}
value={username} value={username}
id="username"
placeholder="username" placeholder="username"
className="w-32 pl-2 pb-1 border-b border-gray-600 placeholder-gray-500" className="w-32 pl-2 pb-1 border-b border-gray-600 placeholder-gray-500"
/> />

View File

@ -1,14 +0,0 @@
import { SessionContextProvider } from './SessionContext'
interface IContext {
children: React.ReactNode
}
function AppContext(props: IContext): any {
const { children } = props
const providers = [SessionContextProvider]
const res = providers.reduceRight((acc, CurrVal) => <CurrVal>{acc as any}</CurrVal>, children)
return res as any
}
export default AppContext

View File

@ -1,80 +0,0 @@
import { createContext, useContext, useState } from 'react'
type Setter<T> = React.Dispatch<React.SetStateAction<T | undefined>>
type MustSetter<T> = React.Dispatch<React.SetStateAction<T>>
import useLocalStorage from 'use-local-storage'
import { BasicColumns, ColumnInfo, ColumnName, DetailsColumns, MoveColumns } from '../lib/columns'
import { OrderTracker } from '../lib/lifeto/order_manager'
import { StoreColSet } from '../lib/storage'
import { ColumnSet } from '../lib/table'
interface SessionContextProps {
orders: OrderTracker
activeTable: string
screen: string
columns: ColumnSet
tags: ColumnSet
dirty: number
currentSearch: string
setActiveTable: Setter<string>
setScreen: Setter<string>
setDirty: MustSetter<number>
setCurrentSearch: MustSetter<string>
}
const _defaultColumn: (ColumnInfo | ColumnName)[] = [
...BasicColumns,
...MoveColumns,
...DetailsColumns,
]
const SessionContext = createContext({} as SessionContextProps)
const dotry = (x: any, d: any) => {
try {
return x()
} catch {
return d
}
}
export const SessionContextProvider = ({ children }: { children: any }) => {
const [activeTable, setActiveTable] = useLocalStorage<string>('activeTable', '')
const [screen, setScreen] = useLocalStorage<string>('screen', '')
const [columns] = useState<ColumnSet>(new ColumnSet(_defaultColumn))
const [tags] = useState<ColumnSet>(dotry(() => StoreColSet.Revive('tags'), new ColumnSet()))
const [orders] = useState<OrderTracker>(new OrderTracker())
const [dirty, setDirty] = useState<number>(0)
const [currentSearch, setCurrentSearch] = useState<string>('')
return (
<SessionContext.Provider
value={{
orders,
activeTable,
screen,
columns,
tags,
dirty,
currentSearch,
setActiveTable,
setScreen,
setDirty,
setCurrentSearch,
}}
>
{children}
</SessionContext.Provider>
)
}
export const useSessionContext = (): SessionContextProps => {
const context = useContext<SessionContextProps>(SessionContext)
if (context === null) {
throw new Error('"useSessionContext" should be used inside a "SessionContextProvider"')
}
return context
}

View File

@ -1,7 +1,6 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import { App } from './App' import { App } from './App'
import AppContext from './context/AppContext'
import './lib/superjson' import './lib/superjson'
import './index.css' import './index.css'
@ -14,9 +13,7 @@ ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<Provider> <Provider>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<AppContext> <App />
<App />
</AppContext>
</QueryClientProvider> </QueryClientProvider>
</Provider> </Provider>
</React.StrictMode>, </React.StrictMode>,

View File

@ -88,15 +88,6 @@ class Count implements ColumnInfo {
} }
const spacer = '-----------' const spacer = '-----------'
class Move implements ColumnInfo {
name: ColumnName = 'Move'
displayName = 'Target'
writable = true
options = getMoveTargets
getter(_item: TricksterItem): string | number {
return spacer
}
}
const getMoveTargets = (invs: string[]): string[] => { const getMoveTargets = (invs: string[]): string[] => {
const out: string[] = [] const out: string[] = []
@ -110,6 +101,16 @@ const getMoveTargets = (invs: string[]): string[] => {
return out return out
} }
class Move implements ColumnInfo {
name: ColumnName = 'Move'
displayName = 'Target'
writable = true
options = getMoveTargets
getter(_item: TricksterItem): string | number {
return spacer
}
}
class MoveCount implements ColumnInfo { class MoveCount implements ColumnInfo {
name: ColumnName = 'MoveCount' name: ColumnName = 'MoveCount'
displayName = 'Move #' displayName = 'Move #'

View File

@ -34,9 +34,12 @@ export class LTOApiv0 implements LTOApi {
case 'buy-from-order': case 'buy-from-order':
case 'cancel-order': case 'cancel-order':
endpoint = market_endpoint endpoint = market_endpoint
break
case 'sell-item': case 'sell-item':
VERB = 'POSTFORM' VERB = 'POSTFORM'
break
default: default:
break
} }
return this.s.request(VERB as any, e, t, endpoint).then(x => { return this.s.request(VERB as any, e, t, endpoint).then(x => {
return x.data return x.data

View File

@ -166,10 +166,13 @@ export class InternalXfer extends BasicOrder {
this.originalResponse = x this.originalResponse = x
this.stage = 1 this.stage = 1
this.mark('SUCCESS') this.mark('SUCCESS')
const origin_item = r.invs.value.get(this.details?.origin_path!)?.items[ if (this.details?.origin_path && this.details?.item_uid && this.details?.count) {
this.details?.item_uid! const inventory = r.invs.value.get(this.details.origin_path)
]! const origin_item = inventory?.items[this.details.item_uid]
origin_item.item_count = origin_item.item_count - this.details?.count! if (origin_item) {
origin_item.item_count = origin_item.item_count - this.details.count
}
}
} else { } else {
throw x.message throw x.message
} }
@ -223,10 +226,13 @@ export class BankItem extends BasicOrder {
this.stage = 1 this.stage = 1
this.originalResponse = x this.originalResponse = x
this.mark('SUCCESS') this.mark('SUCCESS')
const origin_item = r.invs.value.get(this.details?.origin_path!)?.items[ if (this.details?.origin_path && this.details?.item_uid && this.details?.count) {
this.details?.item_uid! const inventory = r.invs.value.get(this.details.origin_path)
]! const origin_item = inventory?.items[this.details.item_uid]
origin_item.item_count = origin_item.item_count - this.details?.count! if (origin_item) {
origin_item.item_count = origin_item.item_count - this.details.count
}
}
} else { } else {
throw x.message ? x.message : 'unknown error' throw x.message ? x.message : 'unknown error'
} }
@ -292,12 +298,13 @@ export class PrivateMarket extends BasicOrder {
this.mark('SUCCESS') this.mark('SUCCESS')
this.listingId = x.data.listing_id this.listingId = x.data.listing_id
this.listingHash = x.data.hash this.listingHash = x.data.hash
try { if (this.details?.origin_path && this.details?.item_uid && this.details?.count) {
const origin_item = r.invs.value.get(this.details?.origin_path!)?.items[ const inventory = r.invs.value.get(this.details.origin_path)
this.details?.item_uid! const origin_item = inventory?.items[this.details.item_uid]
]! if (origin_item) {
origin_item.item_count = origin_item.item_count - this.details?.count! origin_item.item_count = origin_item.item_count - this.details.count
} catch (_e) {} }
}
} else { } else {
throw x.message ? x.message : 'unknown error' throw x.message ? x.message : 'unknown error'
} }

View File

@ -65,6 +65,7 @@ export class OrderTracker implements Serializable<OrderTracker> {
break break
case 'MarketMove': case 'MarketMove':
newOrder = new MarketMove(o.details).parse(o) newOrder = new MarketMove(o.details).parse(o)
break
case 'MarketMoveToChar': case 'MarketMoveToChar':
newOrder = new MarketMoveToChar(o.details).parse(o) newOrder = new MarketMoveToChar(o.details).parse(o)
break break
@ -156,8 +157,11 @@ export class OrderSender {
} }
private transformInternalOrder(o: OrderDetails): TxnDetails { private transformInternalOrder(o: OrderDetails): TxnDetails {
const origin = this.chars.get(o.origin_path)! const origin = this.chars.get(o.origin_path)
const target = this.chars.get(o.target_path)! const target = this.chars.get(o.target_path)
if (!origin || !target) {
throw new Error(`Character not found: origin=${o.origin_path}, target=${o.target_path}`)
}
return { return {
origin: origin.id.toString(), origin: origin.id.toString(),
target: target.id.toString(), target: target.id.toString(),

View File

@ -26,14 +26,15 @@ export class StatefulLTOApi implements LTOApi {
} }
GetInventory = async (path: string): Promise<TricksterInventory> => { GetInventory = async (path: string): Promise<TricksterInventory> => {
const inv = await this.u.GetInventory(path) const inv = await this.u.GetInventory(path)
if (this.r.invs.value.get(inv.path)) { const existingInv = this.r.invs.value.get(inv.path)
this.r.invs.value.get(inv.path)!.items = inv.items if (existingInv) {
existingInv.items = inv.items
if (inv.galders) {
existingInv.galders = inv.galders
}
} else { } else {
this.r.invs.value.set(inv.path, inv) this.r.invs.value.set(inv.path, inv)
} }
if (inv.galders) {
this.r.invs.value.get(inv.path)!.galders = inv.galders
}
this.r.dirty.value = this.r.dirty.value + 1 this.r.dirty.value = this.r.dirty.value + 1
return inv return inv
} }

View File

@ -150,16 +150,6 @@ class Count implements ColumnInfo {
} }
} }
class Move implements ColumnInfo {
name: ColumnName = 'Move'
displayName = 'Target'
writable = true
options = getMoveTargets
getter(_item: TricksterItem): string | number {
return '---------------------------------------------'
}
}
const getMoveTargets = (invs: string[]): string[] => { const getMoveTargets = (invs: string[]): string[] => {
const out: string[] = [] const out: string[] = []
for (const k of invs) { for (const k of invs) {
@ -171,6 +161,16 @@ const getMoveTargets = (invs: string[]): string[] => {
return out return out
} }
class Move implements ColumnInfo {
name: ColumnName = 'Move'
displayName = 'Target'
writable = true
options = getMoveTargets
getter(_item: TricksterItem): string | number {
return '---------------------------------------------'
}
}
class MoveCount implements ColumnInfo { class MoveCount implements ColumnInfo {
name: ColumnName = 'MoveCount' name: ColumnName = 'MoveCount'
displayName = 'Move #' displayName = 'Move #'

View File

@ -33,55 +33,62 @@ export interface Session {
request: (verb: Method, url: string, data: any, c?: EndpointCreator) => Promise<any> request: (verb: Method, url: string, data: any, c?: EndpointCreator) => Promise<any>
} }
export class LoginHelper { export const login = async (user: string, pass: string): Promise<TokenSession> => {
static login = async (user: string, pass: string): Promise<TokenSession> => { return axios
return axios .get(login_endpoint('login'), {
.get(login_endpoint('login'), { withCredentials: false,
withCredentials: false, maxRedirects: 0,
maxRedirects: 0, xsrfCookieName: 'XSRF-TOKEN',
xsrfCookieName: 'XSRF-TOKEN', })
}) .then(async () => {
.then(async () => { return axios.post(
return axios.post( login_endpoint('login'),
login_endpoint('login'), {
{ login: user,
login: user, password: pass,
password: pass, redirectTo: 'lifeto',
redirectTo: 'lifeto', },
}, {
{ withCredentials: false,
withCredentials: false, maxRedirects: 0,
maxRedirects: 0, xsrfCookieName: 'XSRF-TOKEN',
xsrfCookieName: 'XSRF-TOKEN', },
}, )
) })
}) .then(async () => {
.then(async () => { return new TokenSession()
return new TokenSession() })
}) .catch(e => {
.catch(e => { if (e instanceof AxiosError) {
if (e instanceof AxiosError) { if (e.code === 'ERR_BAD_REQUEST') {
if (e.code === 'ERR_BAD_REQUEST') { throw new Error('invalid username/password')
throw 'invalid username/password'
}
throw e.message
} }
throw e throw new Error(e.message)
}) }
} throw e
static info = async (): Promise<TricksterAccountInfo> => { })
return axios }
.get(raw_endpoint('settings/info'), { withCredentials: false })
.then((ans: AxiosResponse) => { export const getAccountInfo = async (): Promise<TricksterAccountInfo> => {
return ans.data return axios
}) .get(raw_endpoint('settings/info'), { withCredentials: false })
} .then((ans: AxiosResponse) => {
static logout = async (): Promise<void> => { return ans.data
return axios })
.get(login_endpoint('logout'), { withCredentials: false }) }
.catch(() => {})
.then(() => {}) export const logout = async (): Promise<void> => {
} return axios
.get(login_endpoint('logout'), { withCredentials: false })
.catch(() => {})
.then(() => {})
}
// Keep LoginHelper for backwards compatibility
export const LoginHelper = {
login,
info: getAccountInfo,
logout,
} }
export class TokenSession implements Session { export class TokenSession implements Session {
@ -91,7 +98,7 @@ export class TokenSession implements Session {
data: any, data: any,
c: EndpointCreator = api_endpoint, c: EndpointCreator = api_endpoint,
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
let promise let promise: Promise<AxiosResponse>
switch (verb.toLowerCase()) { switch (verb.toLowerCase()) {
case 'post': case 'post':
promise = axios.post(c(url), data, this.genHeaders()) promise = axios.post(c(url), data, this.genHeaders())

View File

@ -20,7 +20,8 @@ const columns = {
return c[0].has(row.original.item.id) return c[0].has(row.original.item.id)
}, [c]) }, [c])
return ( return (
<div <button
type="button"
className={`no-select flex flex-row ${row.original.status?.selected ? 'animate-pulse' : ''}`} className={`no-select flex flex-row ${row.original.status?.selected ? 'animate-pulse' : ''}`}
onClick={_e => { onClick={_e => {
setItemSelection({ setItemSelection({
@ -35,7 +36,7 @@ const columns = {
className="select-none object-contain select-none" className="select-none object-contain select-none"
/> />
</div> </div>
</div> </button>
) )
}, },
}), }),

View File

@ -187,7 +187,7 @@ export const currentItemSelectionAtom = atom<[Map<string, number>, number]>([
export const currentInventorySearchQueryAtom = atom('') export const currentInventorySearchQueryAtom = atom('')
export const filteredCharacterItemsAtom = atom(get => { export const filteredCharacterItemsAtom = atom(get => {
const { items, searcher } = get(currentCharacterItemsAtom) const { items } = get(currentCharacterItemsAtom)
const [selection] = get(currentItemSelectionAtom) const [selection] = get(currentItemSelectionAtom)
const filter = get(inventoryFilterAtom) const filter = get(inventoryFilterAtom)
const out: ItemWithSelection[] = [] const out: ItemWithSelection[] = []
@ -202,7 +202,7 @@ export const filteredCharacterItemsAtom = atom(get => {
continue continue
} }
} }
let status let status: { selected: boolean } | undefined
if (selection.has(value.id)) { if (selection.has(value.id)) {
status = { status = {
selected: true, selected: true,

View File

@ -68,7 +68,7 @@ export const loadStore = () => {
export const saveStore = () => { export const saveStore = () => {
const store = useStoreRef() const store = useStoreRef()
for (const [k, v] of Object.entries(StoreReviver)) { for (const [k, v] of Object.entries(StoreReviver)) {
let coke let coke: string | undefined
if (store[k as keyof RefStore] !== undefined) { if (store[k as keyof RefStore] !== undefined) {
coke = v.Murder(store[k as keyof RefStore].value as any) coke = v.Murder(store[k as keyof RefStore].value as any)
} }