diff --git a/index.html b/index.html index 11603f8..9ce4f41 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ Vite App - +
diff --git a/package-lock.json b/package-lock.json index 65927e7..30503f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "@vueuse/core": "^8.7.5", "axios": "^0.27.2", "handsontable": "^12.0.1", + "loglevel": "^1.8.0", + "pinia": "^2.0.14", "qs": "^6.10.5", "typescript-cookie": "^1.0.3", "vue": "^3.2.25", @@ -173,6 +175,11 @@ "@vue/shared": "3.2.37" } }, + "node_modules/@vue/devtools-api": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.0.tgz", + "integrity": "sha512-pF1G4wky+hkifDiZSWn8xfuLOJI1ZXtuambpBEYaf7Xaf6zC/pM29rvAGpd3qaGXnr4BAXU1Pxz/VfvBGwexGA==" + }, "node_modules/@vue/reactivity": { "version": "3.2.37", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", @@ -882,6 +889,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -963,6 +982,56 @@ "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.0.tgz", "integrity": "sha512-SgGxMYX0NHj9oQnMaSyAipr2gOrbB4Lfs/TJTb6H6hRHs39/5c5VZi73Q8hr53+vWjdn6HzkWcj8Vtl3c9ziaA==" }, + "node_modules/pinia": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.14.tgz", + "integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==", + "dependencies": { + "@vue/devtools-api": "^6.1.4", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.1.tgz", + "integrity": "sha512-xmkJ56koG3ptpLnpgmIzk9/4nFf4CqduSJbUM0OdPoU87NwRuZ6x49OLhjSa/fC15fV+5CbEnrxU4oyE022svg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -1100,7 +1169,7 @@ "version": "4.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1331,6 +1400,11 @@ "@vue/shared": "3.2.37" } }, + "@vue/devtools-api": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.0.tgz", + "integrity": "sha512-pF1G4wky+hkifDiZSWn8xfuLOJI1ZXtuambpBEYaf7Xaf6zC/pM29rvAGpd3qaGXnr4BAXU1Pxz/VfvBGwexGA==" + }, "@vue/reactivity": { "version": "3.2.37", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", @@ -1740,6 +1814,11 @@ "has": "^1.0.3" } }, + "loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==" + }, "magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -1800,6 +1879,23 @@ "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.0.tgz", "integrity": "sha512-SgGxMYX0NHj9oQnMaSyAipr2gOrbB4Lfs/TJTb6H6hRHs39/5c5VZi73Q8hr53+vWjdn6HzkWcj8Vtl3c9ziaA==" }, + "pinia": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.14.tgz", + "integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==", + "requires": { + "@vue/devtools-api": "^6.1.4", + "vue-demi": "*" + }, + "dependencies": { + "vue-demi": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.1.tgz", + "integrity": "sha512-xmkJ56koG3ptpLnpgmIzk9/4nFf4CqduSJbUM0OdPoU87NwRuZ6x49OLhjSa/fC15fV+5CbEnrxU4oyE022svg==", + "requires": {} + } + } + }, "postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -1891,7 +1987,7 @@ "version": "4.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", - "dev": true + "devOptional": true }, "typescript-cookie": { "version": "1.0.3", diff --git a/package.json b/package.json index d0b6aa9..0100f4d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "@vueuse/core": "^8.7.5", "axios": "^0.27.2", "handsontable": "^12.0.1", + "loglevel": "^1.8.0", + "pinia": "^2.0.14", "qs": "^6.10.5", "typescript-cookie": "^1.0.3", "vue": "^3.2.25", diff --git a/src/App.vue b/src/App.vue index 8d4940e..c1123d0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,13 +1,25 @@ - + + diff --git a/src/ColumnCheckbox.vue b/src/ColumnCheckbox.vue new file mode 100644 index 0000000..e66ce19 --- /dev/null +++ b/src/ColumnCheckbox.vue @@ -0,0 +1,35 @@ + + + + diff --git a/src/components/CharacterCard.vue b/src/components/CharacterCard.vue new file mode 100644 index 0000000..9e4c9be --- /dev/null +++ b/src/components/CharacterCard.vue @@ -0,0 +1,37 @@ + + + + diff --git a/src/components/CharacterInventory.vue b/src/components/CharacterInventory.vue index 2c7dea2..bc06bb6 100644 --- a/src/components/CharacterInventory.vue +++ b/src/components/CharacterInventory.vue @@ -1,62 +1,51 @@ - - + + + diff --git a/src/components/CharacterRoulette.vue b/src/components/CharacterRoulette.vue new file mode 100644 index 0000000..6cbe0ea --- /dev/null +++ b/src/components/CharacterRoulette.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/components/ColumnCheckbox.vue b/src/components/ColumnCheckbox.vue new file mode 100644 index 0000000..e66ce19 --- /dev/null +++ b/src/components/ColumnCheckbox.vue @@ -0,0 +1,35 @@ + + + + diff --git a/src/components/ColumnCheckboxGroup.vue b/src/components/ColumnCheckboxGroup.vue new file mode 100644 index 0000000..9c1e10f --- /dev/null +++ b/src/components/ColumnCheckboxGroup.vue @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/FilterCheckbox.vue b/src/components/FilterCheckbox.vue new file mode 100644 index 0000000..d6a3184 --- /dev/null +++ b/src/components/FilterCheckbox.vue @@ -0,0 +1,35 @@ + + + + diff --git a/src/components/FilterCheckboxGroup.vue b/src/components/FilterCheckboxGroup.vue new file mode 100644 index 0000000..c3d8e6a --- /dev/null +++ b/src/components/FilterCheckboxGroup.vue @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/SessionDisplay.vue b/src/components/SessionDisplay.vue deleted file mode 100644 index f7dcf63..0000000 --- a/src/components/SessionDisplay.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue new file mode 100644 index 0000000..b7c0b35 --- /dev/null +++ b/src/components/Sidebar.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/src/lib/columns.ts b/src/lib/columns.ts index aa84c8b..15c8f18 100644 --- a/src/lib/columns.ts +++ b/src/lib/columns.ts @@ -5,13 +5,62 @@ import { TricksterInventory, TricksterItem } from "./trickster" import Core from "handsontable/core"; -type DefaultColumnName = "Image"|"Name"|"Count" -type ActionColumnName = "Move"|"MoveCount"|"ConfirmMove" -type TagColumName = "Equip"|"Drill"|"Card"|"Quest"|"Consume" -type StatColumnName = "AP"|"Gun AP"|"AC"|"DX"|"MP"|"MA"|"MD"|"WT"|"DA"|"LK"|"HP"|"DP"|"HV" -type EquipmentColumnName = "MinLvl"|"Slots"|"RefineNumber"|"RefineState" -type ColumnName = DefaultColumnName | TagColumName | StatColumnName | ActionColumnName | EquipmentColumnName +export const BasicColumns = [ + "Image","Name","Count", +] +export const DetailsColumns = [ + "Desc","Use", +] + +export const MoveColumns = [ + "Move","MoveCount", +] + +export const TagColumns = [ + "Equip","Drill","Card","Quest","Consume" +] + +export const HackColumns = [ + "All" +] + +export const EquipmentColumns = [ + "MinLvl","Slots","RefineNumber","RefineState", +] + +export const StatsColumns = [ + "AP","GunAP","AC","DX","MP","MA","MD","WT","DA","LK","HP","DP","HV", +] + +export const ColumnNames = [ + ...BasicColumns, + ...MoveColumns, + ...DetailsColumns, + ...EquipmentColumns, + ...StatsColumns, + ...TagColumns, +] + +export type ColumnName = typeof ColumnNames[number] + +const c = (a:ColumnName | ColumnInfo):ColumnName => { + switch(typeof a) { + case "string": + return a + case "object": + return a.name + } +} +export const LazyColumn = c; +export const ColumnSorter = (a:ColumnName | ColumnInfo, b: ColumnName | ColumnInfo):number => { + let n1 = ColumnNames.indexOf(c(a)) + let n2 = ColumnNames.indexOf(c(b)) + if(n1 == n2) { + return 0 + } + return n1 > n2 ? 1 : -1 +} export interface ColumnInfo { name: ColumnName @@ -132,15 +181,6 @@ function moveCountRenderer(instance:Core, td:any, row:number, col:number, prop:a } -class ConfirmMove implements ColumnInfo { - name:ColumnName = "ConfirmMove" - displayName = "Confirm" - writable = true - getter(item:TricksterItem):(string|number){ - return 0 - } -} - class Equip implements ColumnInfo { name:ColumnName = "Equip" displayName = "equip" @@ -157,6 +197,14 @@ class Drill implements ColumnInfo { } } +class All implements ColumnInfo { + name:ColumnName = "All" + displayName = "all" + getter(_:TricksterItem):(string|number){ + return -10000 + } +} + class Card implements ColumnInfo { name:ColumnName = "Card" displayName = "card" @@ -202,7 +250,7 @@ class AP implements ColumnInfo { } class GunAP implements ColumnInfo { - name:ColumnName = "Gun AP" + name:ColumnName = "GunAP" displayName = "Gun AP" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["Gun AP"] : "" @@ -301,7 +349,7 @@ class MinLvl implements ColumnInfo { displayName = "lvl" getter(item:TricksterItem):(string|number){ //TODO: - return 0 + return item.item_min_level? item.item_min_level:"" } } @@ -310,13 +358,13 @@ class Slots implements ColumnInfo { displayName = "slots" getter(item:TricksterItem):(string|number){ //TODO: - return 0 + return item.item_slots ? item.item_slots : "" } } class RefineNumber implements ColumnInfo { name:ColumnName = "RefineNumber" - displayName = "R#" + displayName = "refine" getter(item:TricksterItem):(string|number){ return item.refine_level ? item.refine_level : 0 } @@ -324,31 +372,84 @@ class RefineNumber implements ColumnInfo { class RefineState implements ColumnInfo { name:ColumnName = "RefineState" - displayName = "State" + displayName = "bork" getter(item:TricksterItem):(string|number){ return item.refine_state ? item.refine_state : 0 } } +class Desc implements ColumnInfo { + name:ColumnName = "Desc" + displayName = "desc" + renderer = descRenderer + getter(item:TricksterItem):(string|number){ + return item.item_desc + } +} +function descRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) { + const stringifiedValue = Handsontable.helper.stringify(value); + let showText = stringifiedValue; + const div= document.createElement('div'); + div.innerHTML = showText + div.title = showText + div.style.maxWidth = "30ch" + div.style.textOverflow = "ellipsis" + div.style.overflow= "hidden" + div.style.whiteSpace= "nowrap" + Handsontable.dom.addEvent(div, 'mousedown', event =>{ + event!.preventDefault(); + }); + Handsontable.dom.empty(td); + td.appendChild(div); + td.classList.add("htLeft") +} + +class Use implements ColumnInfo { + name:ColumnName = "Use" + displayName = "use" + renderer= useRenderer; + getter(item:TricksterItem):(string|number){ + return item.item_use + } +} +function useRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) { + const stringifiedValue = Handsontable.helper.stringify(value); + let showText = stringifiedValue; + const div= document.createElement('div'); + div.title = showText + div.innerHTML = showText + div.style.maxWidth = "30ch" + div.style.textOverflow = "ellipsis" + div.style.overflow= "hidden" + div.style.whiteSpace= "nowrap" + Handsontable.dom.addEvent(div, 'mousedown', event =>{ + event!.preventDefault(); + }); + Handsontable.dom.empty(td); + td.appendChild(div); + td.classList.add("htLeft") +} + + + + export const ColumnByNames = (...n:ColumnName[]) => { return n.map(ColumnByName) } - - export const ColumnByName = (n:ColumnName) => { return Columns[n] } - -export const Columns = { +export const Columns:{[Property in ColumnName]:ColumnInfo}= { + Use: new Use(), + Desc: new Desc(), Image: new Image(), Name: new Name(), Count: new Count(), Move: new Move(), MoveCount: new MoveCount(), - ConfirmMove: new ConfirmMove(), Equip: new Equip(), Drill: new Drill(), Card: new Card(), @@ -356,7 +457,6 @@ export const Columns = { Consume: new Consume(), AP: new AP(), GunAP: new GunAP(), - 'Gun AP': new GunAP(), AC: new AC(), DX: new DX(), MP: new MP(), @@ -372,33 +472,5 @@ export const Columns = { Slots: new Slots(), RefineNumber: new RefineNumber(), RefineState: new RefineState(), - image: new Image(), - name: new Name(), - count: new Count(), - move: new Move(), - movecount: new MoveCount(), - confirmmove: new ConfirmMove(), - equip: new Equip(), - drill: new Drill(), - card: new Card(), - quest: new Quest(), - consume: new Consume(), - ap: new AP(), - gunap: new GunAP(), - 'gun ap': new GunAP(), - ac: new AC(), - dx: new DX(), - mp: new MP(), - ma: new MA(), - md: new MD(), - wt: new WT(), - da: new DA(), - lk: new LK(), - hp: new HP(), - dp: new DP(), - hv: new HV(), - minlvl: new MinLvl(), - slots: new Slots(), - refinenumber: new RefineNumber(), - refinestate: new RefineState(), + All: new All(), } diff --git a/src/lib/lifeto.ts b/src/lib/lifeto.ts new file mode 100644 index 0000000..a1421c1 --- /dev/null +++ b/src/lib/lifeto.ts @@ -0,0 +1,105 @@ +import { Axios, AxiosResponse } from "axios" +import log from "loglevel" +import { Session } from "./session" +import { TricksterInventory, TricksterItem, TricksterWallet } from "./trickster" + + +export interface LTOApi { + GetCharacters:(name:string)=>Promise> + GetAccounts:() =>Promise> + GetLoggedin: ()=>Promise +} + +export class LTOApiv0 implements LTOApi { + + s: Session + + constructor(s:Session) { + this.s = s + } + GetCharacters = async (account: string):Promise> =>{ + return this.s.authed_request("GET", `item-manager/items/account/${account}`,undefined).then(async (ans:AxiosResponse)=>{ + const o = ans.data + log.debug("GetCharacters", o) + let out = [{ + name: account, + id: ":" + 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=>{ + v.unique_id = Number(k) + return v + }), + } as TricksterInventory] + 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 =>{ + if(char_path.startsWith(":")) { + char_path = char_path.replace(":","") + } + return this.s.authed_request("GET", `item-manager/items/account/${char_path}`,undefined).then((ans:AxiosResponse)=>{ + const o = ans.data + log.debug("GetInventory", o) + let name = "" + let id = "" + let wallet:TricksterWallet = {galders:0,state:0,job_img:""} + if(char_path.includes("/")) { + let [char, val] = Object.entries(o.characters)[0] as [string,any] + name = val.name + id = char + wallet = { + galders:val.galders as number, + state:val.state as number, + job_img: val.job_img as string + } + }else { + name = char_path + id = char_path + wallet = { + galders: 0, + state: 0, + job_img: "bank", + } + } + let out = { + name, + id, + wallet, + path: char_path, + items:(Object.entries(o.items) as any).map(([k, v]: [string, TricksterItem]):TricksterItem=>{ + v.unique_id = Number(k) + return v + }), + } as TricksterInventory + return out + }) + } + + GetAccounts = async ():Promise => { + return this.s.authed_request("POST", "accounts/list",undefined).then((ans:AxiosResponse)=>{ + log.debug("GetAccounts", ans.data) + return ans.data.map((x:any)=>x.name) + }) + } + GetLoggedin = async ():Promise => { + return this.s.authed_request("POST", "accounts/list",undefined).then((ans:AxiosResponse)=>{ + if(ans.status == 401) { + return false + } + if(ans.status == 200) { + return true + } + return false + }) + } +} diff --git a/src/lib/session.ts b/src/lib/session.ts index 97d9cac..efe6bd5 100644 --- a/src/lib/session.ts +++ b/src/lib/session.ts @@ -1,4 +1,4 @@ -import axios from "axios"; +import axios, { AxiosResponse, Method } from "axios"; import qs from "qs"; import { getCookie, removeCookie } from "typescript-cookie"; @@ -6,15 +6,20 @@ export interface Session { user:string xsrf:string csrf:string - lto:string + authed_request:(verb:Method,url:string,data:any)=>Promise } -export const API_ROOT = "/lifeto/" +export const SITE_ROOT = "/lifeto/" +export const API_ROOT = "api/lifeto/" -const endpoint = (name:string)=>{ - return API_ROOT + name +const login_endpoint = (name:string)=>{ + return SITE_ROOT + name } +const api_endpoint = (name:string)=>{ + return SITE_ROOT+API_ROOT + name +} + export class LoginHelper { user:string @@ -26,9 +31,8 @@ export class LoginHelper { } login = async ():Promise =>{ removeCookie("XSRF-TOKEN") - removeCookie("lifeto_session", {path:'/'}) await sleep(1000) - return axios.get(endpoint("login"),{withCredentials:false}) + return axios.get(login_endpoint("login"),{withCredentials:false}) .then(async (x)=>{ console.log(x) if(x.data){ @@ -37,7 +41,7 @@ export class LoginHelper { }catch(e){ } } - return axios.post(endpoint("login"),{ + return axios.post(login_endpoint("login"),{ login:this.user, password:this.pass, redirectTo:"lifeto" @@ -45,41 +49,38 @@ export class LoginHelper { .then(async (x)=>{ await sleep(100) let xsrf= getCookie("XSRF-TOKEN") - let lifeto = getCookie("lifeto_session") - return new TokenSession(this.user,this.csrf!, xsrf!, lifeto!) + return new TokenSession(this.user,this.csrf!, xsrf!) }) }) } - } const sleep= async(ms:number)=> { return new Promise(resolve => setTimeout(resolve, ms)) } -export class TokenSession { + +export class TokenSession implements Session { csrf:string xsrf:string - lto:string user:string - constructor(name:string, csrf:string, xsrf: string, lifeto:string){ + constructor(name:string, csrf:string, xsrf: string){ this.user = name this.csrf = csrf this.xsrf = xsrf; - this.lto = lifeto; } - authed_request = async (verb:string,url:string,data:any) => { + authed_request = async (verb:string,url:string,data:any):Promise => { let promise - switch (verb){ + switch (verb.toLowerCase()){ case "post": - promise = axios.post(endpoint(url),data,this.genHeaders()) + promise = axios.post(api_endpoint(url),data,this.genHeaders()) break; case "postraw": const querystring = qs.stringify(data) - promise = axios.post(endpoint(url),querystring,this.genHeaders()) + promise = axios.post(api_endpoint(url),querystring,this.genHeaders()) break; case "get": default: - promise = axios.get(endpoint(url),this.genHeaders()) + promise = axios.get(api_endpoint(url),this.genHeaders()) } return promise.then(x=>{ if(x.data){ @@ -93,7 +94,6 @@ export class TokenSession { return y.split("=")[1].split(";")[0]; }) this.xsrf = cookies[0] - this.lto = cookies[1] } return x }) @@ -101,19 +101,8 @@ export class TokenSession { genHeaders = ()=>{ const out = { headers:{ - Cookie:`XSRF-TOKEN=${this.xsrf}; lifeto_session=${this.lto}`, - Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", - Connection: "keep-alive", - "X-Requested-With":"XMLHttpRequest", - Host: "beta.lifeto.co", - "Alt-Used": "beta.lifeto.co", + Accept: "application/json", "Update-Insecure-Requests": 1, - "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0", - "Sec-Fetch-Mode":"navigate", - "Sec-Fetch-Dest":"document", - "Sec-Fetch-Site":"same-origin", - "Sec-Fetch-User":"?1", - "TE":"trailers" }, withCredentials:true } diff --git a/src/lib/table.ts b/src/lib/table.ts index cd2a1dd..4c7c430 100644 --- a/src/lib/table.ts +++ b/src/lib/table.ts @@ -1,46 +1,137 @@ import { HotTableProps } from "@handsontable/vue3/types" import { TricksterInventory } from "./trickster" -import {ColumnInfo} from "./columns" +import {ColumnInfo, ColumnName, Columns, ColumnSorter, LazyColumn} from "./columns" import { ColumnSettings } from "handsontable/settings" import { PredefinedMenuItemKey } from "handsontable/plugins/contextMenu" +import { ref } from "vue" +export interface InventoryTableOptions { + columns: ColumnSet + tags: ColumnSet +} + +export interface Mappable { + map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; +} + + +export class ColumnSet implements Set, Mappable{ + s: Set = new Set() + size: number; + dirty = ref(0) + constructor(i?:Iterable){ + if(i){ + for (const a of i) { + this.s.add(LazyColumn(a)) + } + } + this.size = 0 + this.mark() + } + map(callbackfn: (value: ColumnInfo, index: number, array: ColumnInfo[]) => U, thisArg?: any): U[] { + return Array.from(this.values()).map(callbackfn, thisArg) + } + [Symbol.iterator](): IterableIterator{ + return this.values() + } + [Symbol.toStringTag] = "ColumnSet"; + entries(): IterableIterator<[ColumnInfo, ColumnInfo]>{ + return Array.from(this.values()).map((x):[ColumnInfo,ColumnInfo]=>{return [x,x]}).values() + } + keys(): IterableIterator{ + return this.values() + } + forEach(callbackfn: (value: ColumnInfo, value2: ColumnInfo, set: Set) => void, thisArg?: any): void{ + Array.from(this.values()).forEach((v)=>{ + if(this.has(v)) { + callbackfn(v, v, new Set(this.values())) + } + }, thisArg) + } + values(): IterableIterator{ + return Array.from(this.s.values()).sort(ColumnSorter).map((a, b)=>{ + return Columns[a] + }).values() + } + mark() { + this.dirty.value = this.dirty.value + 1 + this.size = this.s.size + } + add(value: ColumnInfo | ColumnName): this { + this.mark() + this.s.add(LazyColumn(value)) + return this + } + clear(): void { + this.mark() + this.s.clear() + } + delete(value: ColumnInfo | ColumnName): boolean{ + this.mark() + return this.s.delete(LazyColumn(value)) + } + has(value: ColumnInfo | ColumnName): boolean{ + return this.s.has(LazyColumn(value)) + } +} export class InventoryTable { - inventory:TricksterInventory - columns: ColumnInfo[] + inv!:TricksterInventory + o!: InventoryTableOptions - constructor(inv:TricksterInventory, ...columns:ColumnInfo[]) { - this.inventory = inv - this.columns = columns + constructor(inv:TricksterInventory, o:InventoryTableOptions) { + this.setInv(inv) + this.setOptions(o) + } + setOptions(o:InventoryTableOptions) { + this.o = o + } + setInv(inv:TricksterInventory) { + this.inv = inv } - - getTableColumnNames(): string[] { - return this.columns.map(x=>x.displayName) + return this.o.columns.map(x=>x.displayName) } - getTableColumnSettings(): ColumnSettings[] { - return this.columns.map(x=>({ - renderer: x.renderer ? x.renderer : "text", - dropdownMenu: x.filtering ? DefaultDropdownItems() : false, - readOnly: x.writable ? false : true, - selectionMode: x.writable ? "multiple" : "single" - })) + return this.o.columns.map(x=>({ + renderer: x.renderer ? x.renderer : "text", + dropdownMenu: x.filtering ? DefaultDropdownItems() : false, + readOnly: x.writable ? false : true, + selectionMode: x.writable ? "multiple" : "single" + })) } getTableRows():any[][] { - return this.inventory.items.map((item)=>{ - return this.columns.map(x=>x.getter(item)) + return Object.values(this.inv.items) + .filter((item):boolean=>{ + let found = false + if(this.o.tags.s.size > 0) { + let comparer = 1 + for(const tag of this.o.tags.values()) { + let chk = tag.getter(item) + if(chk === comparer) { + found = true + } + } + }else { + found = true + } + return found + }) + .map((item)=>{ + return this.o.columns.map(x=>{ + return x.getter(item) + }) }) } - BuildTable():TableRecipe { const s = DefaultSettings() + const dat = this.getTableRows() return { - data: this.getTableRows(), + data: dat, settings: { - data: this.getTableRows(), + data: dat, colHeaders:this.getTableColumnNames(), columns:this.getTableColumnSettings(), ...s @@ -71,6 +162,7 @@ export const DefaultSettings = ():HotTableProps=>{ indicator: true, headerAction: true, }, + className: 'htLeft', contextMenu: false, readOnlyCellClassName: "", licenseKey:"non-commercial-and-evaluation", diff --git a/src/lib/trickster.ts b/src/lib/trickster.ts index b4a44ed..070e555 100644 --- a/src/lib/trickster.ts +++ b/src/lib/trickster.ts @@ -11,6 +11,10 @@ export interface TricksterItem { item_name: string; item_id: number; item_count: number; + item_desc: string; + item_use: string; + item_slots?: number; + item_min_level?: number; is_equip?: boolean; is_drill?: boolean; item_expire_time?: ItemExpireTime; @@ -29,9 +33,10 @@ export interface TricksterWallet { export interface TricksterInventory { name:string - + path:string + id:string wallet?:TricksterWallet - items:TricksterItem[] + items:{[key:string]:TricksterItem} } @@ -39,14 +44,11 @@ export interface TricksterInventory { export const SampleData:{[key:string]:TricksterInventory} = { aysheBoyfriend: { name: sampleAyshe.characters[100047311].name, - items: Object.entries(sampleAyshe.items).map(([k, v]:[string, any])=>{ - v.unique_id = k - return v - }), + items: sampleAyshe.items, wallet: { galders: sampleAyshe.characters[100047311].galders, job_img: sampleAyshe.characters[100047311].job_img, state: sampleAyshe.characters[100047311].state } - } + } as any } diff --git a/src/main.ts b/src/main.ts index 07ff8b7..f35a7b0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,8 @@ import App from './App.vue' import { globalCookiesConfig } from "vue3-cookies"; +import { createPinia } from 'pinia'; +import log from 'loglevel'; globalCookiesConfig({ expireTimes: "365d", @@ -12,4 +14,9 @@ globalCookiesConfig({ sameSite: "None", }); -createApp(App).mount('#app') +log.setLevel("debug") + +const pinia = createPinia() +createApp(App). + use(pinia). + mount('#app') diff --git a/src/pages/login.vue b/src/pages/login.vue index 1900210..6e529e3 100644 --- a/src/pages/login.vue +++ b/src/pages/login.vue @@ -1,4 +1,5 @@ diff --git a/src/session_storage.ts b/src/session_storage.ts index 6d9543b..d00a813 100644 --- a/src/session_storage.ts +++ b/src/session_storage.ts @@ -1,5 +1,5 @@ import { getCookie, setCookie} from 'typescript-cookie' -import { Session } from './lib/session' +import { Session, TokenSession } from './lib/session' @@ -10,53 +10,27 @@ export const nameCookie = (...s:string[]):string=>{ } export class Storage { - GetSessions():{[key:string]:Session} { - const all_accounts = getCookie(nameCookie("all_accounts")) - const accounts = all_accounts?.split(",") - let out:{[key:string]:Session} = {}; - for (const account in accounts) { - let tmp = { - user: account, - xsrf: getCookie(nameCookie("xsrf",account))!, - lto: getCookie(nameCookie("lto",account))!, - csrf: getCookie(nameCookie("csrf",account))! - } - out[account] = tmp - } - return out - } - RemoveSessions(...s:Session[]) { - for(const v of s) { - this.RemoveSession(v) + + GetSession():Session { + const {user, xsrf, csrf} = { + user: getCookie(nameCookie("user"))!, + xsrf: getCookie(nameCookie("xsrf"))!, + csrf: getCookie(nameCookie("csrf"))! } + return new TokenSession(user, xsrf, csrf) } - RemoveSession(s:Session) { - const all_accounts = getCookie(nameCookie("all_accounts")) - let accounts = all_accounts?.split(",") - accounts = accounts ? accounts : [] - accounts = [...new Set(accounts)] - accounts = accounts.filter(x=>x!=s.user) - - setCookie(nameCookie("all_accounts"),accounts.join(",")) - setCookie(nameCookie("lto",s.user), "") - setCookie(nameCookie("xsrf",s.user),"") + RemoveSession() { + setCookie(nameCookie("user"),"") + setCookie(nameCookie("xsrf"),"") + setCookie(nameCookie("csrf"),"") } - AddSessions(...s:Session[]) { - for(const v of s) { - this.AddSession(v) - } - } + AddSession(s:Session) { - const all_accounts = getCookie(nameCookie("all_accounts")) - let accounts = all_accounts?.split(",") - accounts = accounts ? accounts : [] - accounts.push(s.user) - accounts = [...new Set(accounts)] - setCookie(nameCookie("lto",s.user), s.lto) - setCookie(nameCookie("xsrf",s.user),s.xsrf) - setCookie(nameCookie("csrf",s.user),s.csrf) + setCookie(nameCookie("user"),s.user) + setCookie(nameCookie("xsrf"),s.xsrf) + setCookie(nameCookie("csrf"),s.csrf) } } diff --git a/src/state/state.ts b/src/state/state.ts new file mode 100644 index 0000000..a336b72 --- /dev/null +++ b/src/state/state.ts @@ -0,0 +1,25 @@ +import { defineStore, storeToRefs } from 'pinia' +import { BasicColumns, ColumnInfo, ColumnName, Columns, DetailsColumns, MoveColumns } from '../lib/columns' +import { ColumnSet } from '../lib/table' +import { TricksterInventory } from '../lib/trickster' + +const _defaultColumn:(ColumnInfo| ColumnName)[] = [ + ...BasicColumns, + ...MoveColumns, + ...DetailsColumns, +] +export const useStore = defineStore('state', { + state: ()=> { + return { + accounts: new Set() as Set, + invs: new Map() as Map, + activeTable: "none", + screen: "default", + columns: new ColumnSet(_defaultColumn), + tags: new ColumnSet(), + } + } +}) +export const useStoreRef = ()=>{return storeToRefs(useStore())}; + + diff --git a/vite.config.ts b/vite.config.ts index 680a3e4..81ed4f1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,10 +12,6 @@ export default defineConfig({ target: "https://beta.lifeto.co/", changeOrigin: true, rewrite: (path) => path.replace(/^\/lifeto/, ''), - hostRewrite: "localhost:3001/lifeto", - cookieDomainRewrite:{ - "*":"", - }, }, } }