filters lol

This commit is contained in:
a 2022-07-03 10:50:41 -05:00
parent 080215183f
commit f206ce3bb2
11 changed files with 290 additions and 75 deletions

View File

@ -29,27 +29,42 @@ import Sidebar from "./components/Sidebar.vue";
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-align: center; text-align: center;
color: #2c3e50; color: #2c3e50;
margin-top: 60px;
height: 100vh; height: 100vh;
overflow-y: hidden; overflow-y: hidden;
} }
.handsontable th {
border-right: 0px !important;
border-left: 0px !important;
border-top: 1px white !important;
border-bottom: 1px white !important;
line-height: 0 !important;
vertical-align: middle !important;
text-align: left !important;
min-width: 10px !important;
width: 20px !important;
}
.handsontable td { .handsontable td {
border-right: 0px !important; border-right: 0px !important;
border-left: 0px !important; border-left: 0px !important;
border-top: 1px white !important; border-top: 1px white !important;
border-bottom: 1px white !important; border-bottom: 1px white !important;
background-color: #F7F7F7 !important; background-color: #F7F7F7 !important;
vertical-align: middle !important;
} }
.handsontable tr { .handsontable tr {
border-radius: 10px !important; border-radius: 10px !important;
} }
.handsontable .changeType {
margin: 0px !important;
border:0px !important;
float: none !important;
}
</style> </style>
<style> <style>
.parent { .parent {
display: grid; display: grid;
height: 100%;
grid-template-columns: 1fr 4fr 3fr; grid-template-columns: 1fr 4fr 3fr;
grid-template-rows: 1fr repeat(2, 3fr) 1fr; grid-template-rows: 1fr repeat(2, 3fr) 1fr;
grid-column-gap: 0px; grid-column-gap: 0px;
@ -64,7 +79,7 @@ import Sidebar from "./components/Sidebar.vue";
.main { .main {
grid-area: 2 / 2 / 5 / 4; grid-area: 2 / 2 / 5 / 4;
overflow-x: scroll; overflow-x: scroll;
overflow-y: scroll; overflow-y: hidden;
} }
.sidebar { .sidebar {

View File

@ -2,35 +2,41 @@
<div> <div>
<div <div
v-on:click="selectCharacter()" v-on:click="selectCharacter()"
>{{name}} the <span v-html="job" /> ({{galders.toLocaleString()}}g)</div> >{{name}} the <span v-html="job" /> ({{galders.toLocaleString()}}g) ({{props.character}})</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
const session = storage.GetSession() const session = storage.GetSession()
const api:LTOApi = new LTOApiv0(session) const api:LTOApi = getLTOState(LTOApiv0, session, useStoreRef())
const props = defineProps(['character']) const props = defineProps(['character'])
const name = ref(props.character.split("/").pop())
const name = ref("")
const job = ref("") const job = ref("")
const galders = ref(0) const galders = ref(0)
const {invs, activeTable} = useStoreRef() const {invs, activeTable} = useStoreRef()
name.value = invs.value.get(props.character)?.name! watch(invs.value,()=>{
job.value = invs.value.get(props.character)?.wallet?.job_img! const currentInv = invs.value.get(props.character)
galders.value = invs.value.get(props.character)?.wallet?.galders! if(currentInv){
if(currentInv.wallet){
name.value = currentInv.name!
job.value = currentInv.wallet?.job_img!
galders.value = currentInv.wallet?.galders!
}
}
},{deep:true})
const selectCharacter = () => { const selectCharacter = () => {
activeTable.value = props.character activeTable.value = props.character
log.info("selected char", activeTable.value) api.GetInventory(props.character)
} }
</script> </script>
<script lang="ts"> <script lang="ts">
import log from 'loglevel'; import log from 'loglevel';
import { defineComponent, computed, PropType, defineProps, defineEmits, ref} from 'vue'; import { defineComponent, computed, PropType, defineProps, defineEmits, ref, watch, onMounted} from 'vue';
import { LTOApi, LTOApiv0 } from '../lib/lifeto'; import { getLTOState, LTOApi, LTOApiv0 } from '../lib/lifeto';
import { LoginHelper, Session } from '../lib/session'; import { LoginHelper, Session } from '../lib/session';
import { storage } from '../session_storage'; import { storage } from '../session_storage';
import { useStore, useStoreRef } from '../state/state'; import { useStore, useStoreRef } from '../state/state';

View File

@ -9,28 +9,32 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { HotTable, HotColumn } from '@handsontable/vue3'; import { HotTable, HotColumn } from '@handsontable/vue3';
const session = storage.GetSession() const {invs, activeTable, columns, tags, dirty} = useStoreRef()
const api:LTOApi = new LTOApiv0(session)
const {invs, activeTable, columns, tags} = useStoreRef()
const hotTableComponent = ref<any>(null) const hotTableComponent = ref<any>(null)
const updateTable = () => { const updateTable = ():TableRecipe | undefined => {
if (invs.value.has(activeTable.value)) { if (invs.value.has(activeTable.value)) {
const chardat = invs.value.get(activeTable.value) const chardat = invs.value.get(activeTable.value)
if (chardat) { if (chardat) {
const it = new InventoryTable(chardat!, { columns: columns.value, tags: tags.value }) const it = new InventoryTable(chardat, { columns: columns.value, tags: tags.value })
const hot = (hotTableComponent.value.hotInstance as Handsontable) const hot = (hotTableComponent.value.hotInstance as Handsontable)
const build = it.BuildTable() const build = it.BuildTable()
hot.updateSettings(build.settings) hot.updateSettings(build.settings)
hot.updateData(build.data) hot.updateData(build.data)
return build
} }
} }
return undefined
} }
// register Handsontable's modules // register Handsontable's modules
registerAllModules(); registerAllModules();
watch([columns.value.dirty,tags.value.dirty, activeTable], () => { watch([columns.value.dirty, tags.value.dirty, activeTable, dirty], () => {
updateTable() log.debug(`${dirty.value} rendering inventory`, activeTable.value)
let u = updateTable()
if(u != undefined){
saveStore()
}
}) })
</script> </script>
@ -42,9 +46,10 @@ import { DefaultSettings, InventoryTable, TableRecipe } from '../lib/table';
import { Columns, ColumnByNames, ColumnInfo } from '../lib/columns'; import { Columns, ColumnByNames, ColumnInfo } from '../lib/columns';
import { TricksterItem, SampleData } from '../lib/trickster'; import { TricksterItem, SampleData } from '../lib/trickster';
import Handsontable from 'handsontable'; import Handsontable from 'handsontable';
import { useStoreRef } from '../state/state'; import { useStoreRef, saveStore } from '../state/state';
import { storage } from '../session_storage'; import { storage } from '../session_storage';
import { LTOApi, LTOApiv0 } from '../lib/lifeto'; import { LTOApi, LTOApiv0 } from '../lib/lifeto';
import log from 'loglevel';
</script> </script>
<style src="handsontable/dist/handsontable.full.css"> <style src="handsontable/dist/handsontable.full.css">

View File

@ -7,33 +7,33 @@
<script lang="ts" setup> <script lang="ts" setup>
import CharacterCard from './CharacterCard.vue'; import CharacterCard from './CharacterCard.vue';
const { accounts, invs } = useStoreRef() const { accounts, invs, activeTable } = useStoreRef()
const characters = ref([] as string[]) const characters = ref([] as string[])
watch(invs, () => { watch(invs, () => {
characters.value = [...new Set([...characters.value, ...invs.value.keys()])] characters.value = [...new Set([...characters.value, ...invs.value.keys()])]
}, { deep: true }) }, { deep: true })
const session = storage.GetSession() const session = storage.GetSession()
const api: LTOApi = new LTOApiv0(session) const api:LTOApi = getLTOState(LTOApiv0, session, useStoreRef())
api.GetAccounts().then(xs => { api.GetAccounts().then(xs => {
xs.forEach(x => { xs.forEach(x => {
accounts.value.add(x) characters.value.push(x)
api.GetCharacters(x).then(chars => {
chars.forEach(char => {
log.debug("new character", char)
invs.value.set(char.path, char)
})
})
}) })
}) })
onMounted(()=>{
let val = invs.value.get(activeTable.value)
if(!val || Object.values(val.items).length == 0) {
api.GetInventory(activeTable.value)
}
})
</script> </script>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed, PropType, defineProps, defineEmits, ref, watch } from 'vue'; import { defineComponent, computed, PropType, defineProps, defineEmits, ref, watch, onMounted } from 'vue';
import { LTOApi, LTOApiv0 } from '../lib/lifeto'; import { getLTOState, LTOApi, LTOApiv0 } from '../lib/lifeto';
import { LoginHelper, Session } from '../lib/session'; import { LoginHelper, Session } from '../lib/session';
import { storage } from '../session_storage'; import { storage } from '../session_storage';
import { useStore, useStoreRef } from '../state/state'; import { useStore, useStoreRef } from '../state/state';

View File

@ -14,15 +14,14 @@ export const DetailsColumns = [
] ]
export const MoveColumns = [ export const MoveColumns = [
"Move","MoveCount", "MoveCount","Move",
] ]
export const TagColumns = [ export const TagColumns = [
"Equip","Drill","Card","Quest","Consume" "All","Equip","Drill","Card","Quest","Consume", "Compound"
] ]
export const HackColumns = [ export const HackColumns = [
"All"
] ]
export const EquipmentColumns = [ export const EquipmentColumns = [
@ -199,7 +198,7 @@ class Drill implements ColumnInfo {
class All implements ColumnInfo { class All implements ColumnInfo {
name:ColumnName = "All" name:ColumnName = "All"
displayName = "all" displayName = "swap"
getter(_:TricksterItem):(string|number){ getter(_:TricksterItem):(string|number){
return -10000 return -10000
} }
@ -216,6 +215,19 @@ class Card implements ColumnInfo {
const cardFilter= (item:TricksterItem): boolean => { const cardFilter= (item:TricksterItem): boolean => {
return (item.item_name.endsWith(" Card") || item.item_name.startsWith("Star Card")) return (item.item_name.endsWith(" Card") || item.item_name.startsWith("Star Card"))
} }
class Compound implements ColumnInfo {
name:ColumnName = "Compound"
displayName = "comp"
getter(item:TricksterItem):(string|number){
return compFilter(item) ? 1 : 0
}
}
const compFilter= (item:TricksterItem): boolean => {
return (item.item_desc.toLowerCase().includes("compound item"))
}
class Quest implements ColumnInfo { class Quest implements ColumnInfo {
name:ColumnName = "Quest" name:ColumnName = "Quest"
@ -238,7 +250,8 @@ class Consume implements ColumnInfo {
} }
const consumeFilter= (item:TricksterItem): boolean => { const consumeFilter= (item:TricksterItem): boolean => {
return false const tl = item.item_use.toLowerCase()
return tl.includes("recover") || tl.includes("restores")
} }
class AP implements ColumnInfo { class AP implements ColumnInfo {
@ -473,4 +486,5 @@ export const Columns:{[Property in ColumnName]:ColumnInfo}= {
RefineNumber: new RefineNumber(), RefineNumber: new RefineNumber(),
RefineState: new RefineState(), RefineState: new RefineState(),
All: new All(), All: new All(),
Compound: new Compound(),
} }

View File

@ -1,48 +1,92 @@
import { Axios, AxiosResponse } from "axios" import { Axios, AxiosResponse } from "axios"
import log from "loglevel" import log from "loglevel"
import { RefStore } from "../state/state"
import { Session } from "./session" import { Session } from "./session"
import { TricksterInventory, TricksterItem, TricksterWallet } from "./trickster" import { dummyChar, TricksterInventory, TricksterItem, TricksterWallet } from "./trickster"
export interface LTOApi { export interface LTOApi {
GetCharacters:(name:string)=>Promise<Array<TricksterInventory>> GetAccount:(name:string) =>Promise<[TricksterInventory, Array<string>]>
GetInventory:(path:string)=>Promise<TricksterInventory>
GetAccounts:() =>Promise<Array<string>> GetAccounts:() =>Promise<Array<string>>
GetLoggedin: ()=>Promise<boolean> GetLoggedin: ()=>Promise<boolean>
} }
export class LTOApiv0 implements LTOApi { export interface SessionBinding {
new(s:Session):LTOApi
}
export const getLTOState = <A extends LTOApi>(c: new (s:Session) => A,s:Session, r:RefStore): LTOApi => {
return new StatefulLTOApi(new c(s),r);
}
export class StatefulLTOApi implements LTOApi {
u: LTOApi
r: RefStore
constructor(s:LTOApi, r:RefStore){
this.u = s
this.r=r
}
GetAccount = async (name:string):Promise<[TricksterInventory, Array<string>]> =>{
const account = await this.u.GetAccount(name)
this.r.invs.value.set(account[0].path,account[0])
account[1].forEach((s)=>{
const d = dummyChar(s)
this.r.invs.value.set(d.path,d)
})
this.r.dirty.value = this.r.dirty.value + 1
return account
}
GetInventory = async (path:string):Promise<TricksterInventory>=>{
if(!path.includes("/")) {
const a = await this.GetAccount(path)
return a[0]
}
const inv = await this.u.GetInventory(path)
this.r.invs.value.set(inv.path,inv)
this.r.dirty.value = this.r.dirty.value + 1
return inv
}
GetAccounts = async ():Promise<Array<string>> =>{
return this.u.GetAccounts()
}
GetLoggedin= async ():Promise<boolean>=>{
return this.u.GetLoggedin()
}
}
export class LTOApiv0 implements LTOApi {
s: Session s: Session
constructor(s:Session) { constructor(s:Session) {
this.s = s this.s = s
} }
GetCharacters = async (account: string):Promise<Array<TricksterInventory>> =>{
GetAccount = async (account: string):Promise<[TricksterInventory, Array<string>]> => {
return this.s.authed_request("GET", `item-manager/items/account/${account}`,undefined).then(async (ans:AxiosResponse)=>{ return this.s.authed_request("GET", `item-manager/items/account/${account}`,undefined).then(async (ans:AxiosResponse)=>{
const o = ans.data const o = ans.data
log.debug("GetCharacters", o) log.debug("GetAccount", o)
let out = [{ let out:string[] = []
Object.entries(o.characters).forEach((x:[string,any])=>{
out.push(`${account}/${x[1].name}`)
})
return [{
name: account, name: account,
id: ":" + account, id:account,
wallet: {
galders: 0,
state: 0,
job_img: "bank",
},
path: account, path: account,
wallet:{
galders:0,
state:0,
job_img: "BANK",
} as TricksterWallet,
items:(Object.entries(o.items) as any).map(([k, v]: [string, TricksterItem]):TricksterItem=>{ items:(Object.entries(o.items) as any).map(([k, v]: [string, TricksterItem]):TricksterItem=>{
v.unique_id = Number(k) v.unique_id = Number(k)
return v return v
}), }),
} as TricksterInventory] },out]
await Promise.all(Object.entries(o.characters).map(async (x:[string,any])=>{
return this.GetInventory(`${account}/${x[1].name}`).then((ans)=>{
out.push(ans)
})
}))
return out
}) })
} }
GetInventory = async (char_path: string):Promise<TricksterInventory> =>{ GetInventory = async (char_path: string):Promise<TricksterInventory> =>{
if(char_path.startsWith(":")) { if(char_path.startsWith(":")) {
char_path = char_path.replace(":","") char_path = char_path.replace(":","")

View File

@ -0,0 +1,49 @@
//class helper {
// Revive<T>(t:string, _type:string):string {
// return t
// }
// Revive<T>(t:string, _type:string[]):string[]{
// return t.split(",")
// }
// Revive<T>(t:string, _type:number):number {
// return Number(t)
// }
// Revive<T>(t:string, _type:number[]):number[]{
// return t.split(",").map(Number)
// }
//}
import { ColumnSet } from "./table"
export const ARRAY_SEPERATOR = ","
let as = ARRAY_SEPERATOR
export interface Reviver<T> {
Murder(t:T):string
Revive(s:string):T
}
export const StoreStr= {
Murder: (s:string):string=>s,
Revive: (s:string):string=>s
}
export const StoreNum = {
Murder: (s:number):string=>s.toString(),
Revive: (s:string):number=>Number(s)
}
export const StoreStrSet = {
Murder: (s:Set<string>):string=>Array.from(s).join(as),
Revive: (s:string):Set<string>=>new Set(s.split(as))
}
export const StoreColSet = {
Murder: (s:ColumnSet):string=>Array.from(s).join(as),
Revive: (s:string):ColumnSet=>new ColumnSet(s.split(as))
}

View File

@ -15,8 +15,6 @@ export interface InventoryTableOptions {
export interface Mappable<T> { export interface Mappable<T> {
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
} }
export class ColumnSet implements Set<ColumnInfo>, Mappable<ColumnInfo>{ export class ColumnSet implements Set<ColumnInfo>, Mappable<ColumnInfo>{
s: Set<ColumnName> = new Set() s: Set<ColumnName> = new Set()
size: number; size: number;
@ -24,7 +22,9 @@ export class ColumnSet implements Set<ColumnInfo>, Mappable<ColumnInfo>{
constructor(i?:Iterable<ColumnInfo | ColumnName>){ constructor(i?:Iterable<ColumnInfo | ColumnName>){
if(i){ if(i){
for (const a of i) { for (const a of i) {
this.s.add(LazyColumn(a)) if(Columns[LazyColumn(a)]){
this.s.add(LazyColumn(a))
}
} }
} }
this.size = 0 this.size = 0
@ -106,12 +106,25 @@ export class InventoryTable {
return Object.values(this.inv.items) return Object.values(this.inv.items)
.filter((item):boolean=>{ .filter((item):boolean=>{
let found = false let found = false
if(this.o.tags.has("All")) {
found = true
}
if(this.o.tags.s.size > 0) { if(this.o.tags.s.size > 0) {
let comparer = 1
for(const tag of this.o.tags.values()) { for(const tag of this.o.tags.values()) {
if(tag.name =="All") {
continue
}
let chk = tag.getter(item) let chk = tag.getter(item)
if(chk === comparer) { if(this.o.tags.has("All")) {
found = true if(chk === 1) {
found = false
break
}
}else {
if(chk === 1) {
found = true
break
}
} }
} }
}else { }else {
@ -152,6 +165,10 @@ export const DefaultSettings = ():HotTableProps=>{
filters: true, filters: true,
manualRowMove: false, manualRowMove: false,
manualColumnMove: false, manualColumnMove: false,
autoColumnSize: {
syncLimit: 1000,
useHeaders: true,
},
allowInsertRow: false, allowInsertRow: false,
allowInsertColumn: false, allowInsertColumn: false,
allowRemoveRow: false, allowRemoveRow: false,
@ -164,6 +181,7 @@ export const DefaultSettings = ():HotTableProps=>{
}, },
className: 'htLeft', className: 'htLeft',
contextMenu: false, contextMenu: false,
dropdownMenu: false,
readOnlyCellClassName: "", readOnlyCellClassName: "",
licenseKey:"non-commercial-and-evaluation", licenseKey:"non-commercial-and-evaluation",
} }

View File

@ -32,13 +32,22 @@ export interface TricksterWallet {
} }
export interface TricksterInventory { export interface TricksterInventory {
name:string
path:string path:string
name:string
id:string id:string
wallet?:TricksterWallet wallet?:TricksterWallet
items:{[key:string]:TricksterItem} items:{[key:string]:TricksterItem}
} }
export const dummyChar = (s:string):TricksterInventory => {
return {
path: s,
name: s.split("/").pop()!,
id: s,
items:{}
}
}
export const SampleData:{[key:string]:TricksterInventory} = { export const SampleData:{[key:string]:TricksterInventory} = {

View File

@ -10,7 +10,6 @@ export const nameCookie = (...s:string[]):string=>{
} }
export class Storage { export class Storage {
GetSession():Session { GetSession():Session {
const {user, xsrf, csrf} = { const {user, xsrf, csrf} = {
user: getCookie(nameCookie("user"))!, user: getCookie(nameCookie("user"))!,
@ -25,8 +24,6 @@ export class Storage {
setCookie(nameCookie("xsrf"),"") setCookie(nameCookie("xsrf"),"")
setCookie(nameCookie("csrf"),"") setCookie(nameCookie("csrf"),"")
} }
AddSession(s:Session) { AddSession(s:Session) {
setCookie(nameCookie("user"),s.user) setCookie(nameCookie("user"),s.user)
setCookie(nameCookie("xsrf"),s.xsrf) setCookie(nameCookie("xsrf"),s.xsrf)

View File

@ -1,7 +1,11 @@
import { defineStore, storeToRefs } from 'pinia' import { defineStore, storeToRefs } from 'pinia'
import { getCookie, setCookie } from 'typescript-cookie'
import { useCookies } from 'vue3-cookies'
import { BasicColumns, ColumnInfo, ColumnName, Columns, DetailsColumns, MoveColumns } from '../lib/columns' import { BasicColumns, ColumnInfo, ColumnName, Columns, DetailsColumns, MoveColumns } from '../lib/columns'
import { Reviver, StoreColSet, StoreStr, StoreStrSet } from '../lib/storage'
import { ColumnSet } from '../lib/table' import { ColumnSet } from '../lib/table'
import { TricksterInventory } from '../lib/trickster' import { TricksterInventory } from '../lib/trickster'
import { nameCookie} from '../session_storage'
const _defaultColumn:(ColumnInfo| ColumnName)[] = [ const _defaultColumn:(ColumnInfo| ColumnName)[] = [
...BasicColumns, ...BasicColumns,
@ -10,16 +14,70 @@ const _defaultColumn:(ColumnInfo| ColumnName)[] = [
] ]
export const useStore = defineStore('state', { export const useStore = defineStore('state', {
state: ()=> { state: ()=> {
const last_table = getCookie(nameCookie("last_table"))
const last_screen = getCookie(nameCookie("last_screen"))
const last_columns = getCookie(nameCookie("last_columns"))?.split(",")
const last_tags = getCookie(nameCookie("last_tags"))?.split(",")
return { return {
accounts: new Set() as Set<string>,
invs: new Map() as Map<string,TricksterInventory>, invs: new Map() as Map<string,TricksterInventory>,
activeTable: "none", accounts: new Set() as Set<string>,
screen: "default", activeTable: last_table ? last_table: "none",
columns: new ColumnSet(_defaultColumn), screen: last_screen ? last_screen : "default",
tags: new ColumnSet(), columns: last_columns ? new ColumnSet([..._defaultColumn,...last_columns]) : new ColumnSet(_defaultColumn),
tags: last_tags ? new ColumnSet(last_tags) : new ColumnSet(),
dirty: 0,
} }
} }
}) })
export const useStoreRef = ()=>{return storeToRefs(useStore())};
export const StoreReviver = {
accounts: StoreStrSet,
activeTable: StoreStr,
screen: StoreStr,
columns: StoreColSet,
tags: StoreColSet,
}
export interface StoreProps {
invs: Map<string,TricksterInventory>
accounts: Set<string>
activeTable: string
screen: string
columns: ColumnSet
tags: ColumnSet
dirty: number
}
export const loadStore = ()=> {
let store = useStoreRef()
for(const [k, v] of Object.entries(StoreReviver)){
const coke = getCookie(nameCookie("last_"+k))
if(coke){
if((store[k as keyof StoreProps]) != undefined){
store[k as keyof StoreProps].value = v.Revive(coke)
}
}
}
}
export const saveStore = ()=> {
let store = useStoreRef()
for(const [k, v] of Object.entries(StoreReviver)){
let coke;
if((store[k as keyof StoreProps]) != undefined){
coke = v.Murder(store[k as keyof StoreProps].value as any)
}
if(coke){
setCookie(nameCookie("last_"+k),coke)
}
}
}
export const useStoreRef = ()=>{
const refs = storeToRefs(useStore())
return refs
};
export type RefStore = ReturnType<typeof useStoreRef>;