forked from a/lifeto-shop
noot
This commit is contained in:
parent
bd20e23b15
commit
f00708e80d
@ -1,4 +1,5 @@
|
|||||||
dist
|
dist
|
||||||
|
dist/**
|
||||||
**/vendor/**
|
**/vendor/**
|
||||||
**/locales/**
|
**/locales/**
|
||||||
generated.*
|
generated.*
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
@ -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>,
|
||||||
|
|||||||
@ -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 #'
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 #'
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user