this is so cracked lol

This commit is contained in:
a 2022-07-03 05:25:12 -05:00
parent b3dad219c6
commit 080215183f
24 changed files with 1114 additions and 273 deletions

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title> <title>Vite App</title>
</head> </head>
<body> <body style="overflow-y: hidden;">
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>

100
package-lock.json generated
View File

@ -14,6 +14,8 @@
"@vueuse/core": "^8.7.5", "@vueuse/core": "^8.7.5",
"axios": "^0.27.2", "axios": "^0.27.2",
"handsontable": "^12.0.1", "handsontable": "^12.0.1",
"loglevel": "^1.8.0",
"pinia": "^2.0.14",
"qs": "^6.10.5", "qs": "^6.10.5",
"typescript-cookie": "^1.0.3", "typescript-cookie": "^1.0.3",
"vue": "^3.2.25", "vue": "^3.2.25",
@ -173,6 +175,11 @@
"@vue/shared": "3.2.37" "@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": { "node_modules/@vue/reactivity": {
"version": "3.2.37", "version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz",
@ -882,6 +889,18 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/magic-string": {
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "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", "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.0.tgz",
"integrity": "sha512-SgGxMYX0NHj9oQnMaSyAipr2gOrbB4Lfs/TJTb6H6hRHs39/5c5VZi73Q8hr53+vWjdn6HzkWcj8Vtl3c9ziaA==" "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": { "node_modules/postcss": {
"version": "8.4.14", "version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
@ -1100,7 +1169,7 @@
"version": "4.7.4", "version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"dev": true, "devOptional": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -1331,6 +1400,11 @@
"@vue/shared": "3.2.37" "@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": { "@vue/reactivity": {
"version": "3.2.37", "version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz",
@ -1740,6 +1814,11 @@
"has": "^1.0.3" "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": { "magic-string": {
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "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", "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.0.tgz",
"integrity": "sha512-SgGxMYX0NHj9oQnMaSyAipr2gOrbB4Lfs/TJTb6H6hRHs39/5c5VZi73Q8hr53+vWjdn6HzkWcj8Vtl3c9ziaA==" "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": { "postcss": {
"version": "8.4.14", "version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
@ -1891,7 +1987,7 @@
"version": "4.7.4", "version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"dev": true "devOptional": true
}, },
"typescript-cookie": { "typescript-cookie": {
"version": "1.0.3", "version": "1.0.3",

View File

@ -14,6 +14,8 @@
"@vueuse/core": "^8.7.5", "@vueuse/core": "^8.7.5",
"axios": "^0.27.2", "axios": "^0.27.2",
"handsontable": "^12.0.1", "handsontable": "^12.0.1",
"loglevel": "^1.8.0",
"pinia": "^2.0.14",
"qs": "^6.10.5", "qs": "^6.10.5",
"typescript-cookie": "^1.0.3", "typescript-cookie": "^1.0.3",
"vue": "^3.2.25", "vue": "^3.2.25",

View File

@ -1,13 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import CharacterInventory from "./components/CharacterInventory.vue" import CharacterInventory from "./components/CharacterInventory.vue"
import Login from "./pages/login.vue" import Login from "./pages/login.vue"
import SessionDisplay from "./components/SessionDisplay.vue"; import CharacterRoulette from "./components/CharacterRoulette.vue";
import Sidebar from "./components/Sidebar.vue";
</script> </script>
<template> <template>
<SessionDisplay/> <div class="parent">
<Login/> <div class="splash"> </div>
<CharacterInventory/> <div class="main">
<CharacterInventory />
</div>
<div class="sidebar">
<Sidebar/>
</div>
<div class="select">
<CharacterRoulette />
</div>
<div class="login">
<Login />
</div>
</div>
</template> </template>
<style> <style>
@ -18,6 +30,8 @@ import SessionDisplay from "./components/SessionDisplay.vue";
text-align: center; text-align: center;
color: #2c3e50; color: #2c3e50;
margin-top: 60px; margin-top: 60px;
height: 100vh;
overflow-y: hidden;
} }
.handsontable td { .handsontable td {
@ -27,7 +41,41 @@ import SessionDisplay from "./components/SessionDisplay.vue";
border-bottom: 1px white !important; border-bottom: 1px white !important;
background-color: #F7F7F7 !important; background-color: #F7F7F7 !important;
} }
.handsontable tr { .handsontable tr {
border-radius: 10px !important; border-radius: 10px !important;
} }
</style> </style>
<style>
.parent {
display: grid;
grid-template-columns: 1fr 4fr 3fr;
grid-template-rows: 1fr repeat(2, 3fr) 1fr;
grid-column-gap: 0px;
grid-row-gap: 0px;
overflow: hidden;
}
.splash {
grid-area: 1 / 1 / 2 / 2;
}
.main {
grid-area: 2 / 2 / 5 / 4;
overflow-x: scroll;
overflow-y: scroll;
}
.sidebar {
grid-area: 2 / 1 / 5 / 2;
}
.selection {
grid-area: 1 / 2 / 2 / 3;
}
.login {
grid-area: 1 / 3 / 2 / 4;
}
</style>

35
src/ColumnCheckbox.vue Normal file
View File

@ -0,0 +1,35 @@
<template>
<input type="checkbox" id={{props.colname}} v-model="checked" />
<label for={{props.colname}}>{{(props.label ? props.label : Columns[props.colname].displayName)}}</label>
</template>
<script lang="ts" setup>
const props = defineProps(["colname","label"])
const {columns} = useStoreRef()
const checked = ref(columns.value.has(props.colname))
watch(columns.value.dirty,()=>{
console.log("changed")
if(columns.value.has(props.colname)) {
checked.value = true
}else{
checked.value = false
}
},{deep:true})
watch(checked, ()=>{
if(checked.value === true) {
columns.value.add(props.colname)
return
}
if(checked.value === false) {
columns.value.delete(props.colname)
return
}
})
</script>
<script lang="ts">
import { defineProps, ref, watch } from 'vue';
import { useStoreRef } from '../state/state';
import { ColumnName, Columns } from '../lib/columns';
</script>

View File

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

View File

@ -1,62 +1,51 @@
<template> <template>
<input v-on:input="swapHotData" placeholder="separate by comma" value="Image, Name, Count, MoveCount, Move, MoveConfirm"> <HotTable
<hot-table
ref="hotTableComponent" ref="hotTableComponent"
:settings="hotSettings" :settings="DefaultSettings()"
></hot-table> ></HotTable>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, computed, PropType, defineProps, defineEmits} from 'vue'; import { ref } from 'vue';
import { HotTable, HotColumn} from '@handsontable/vue3'; import { HotTable, HotColumn } from '@handsontable/vue3';
import { registerAllModules } from 'handsontable/registry';
import { InventoryTable, TableRecipe } from '../lib/table';
import { Columns, ColumnByNames } from '../lib/columns';
import { TricksterItem, SampleData } from '../lib/trickster';
import Handsontable from 'handsontable';
const session = storage.GetSession()
const api:LTOApi = new LTOApiv0(session)
const {invs, activeTable, columns, tags} = useStoreRef()
const hotTableComponent = ref<any>(null)
const updateTable = () => {
if (invs.value.has(activeTable.value)) {
const chardat = invs.value.get(activeTable.value)
if (chardat) {
const it = new InventoryTable(chardat!, { columns: columns.value, tags: tags.value })
const hot = (hotTableComponent.value.hotInstance as Handsontable)
const build = it.BuildTable()
hot.updateSettings(build.settings)
hot.updateData(build.data)
}
}
}
// register Handsontable's modules // register Handsontable's modules
registerAllModules(); registerAllModules();
const _defaultColumn = [ watch([columns.value.dirty,tags.value.dirty, activeTable], () => {
Columns.Image, updateTable()
Columns.Name, })
Columns.Count,
Columns.MoveCount,
Columns.Move,
]
export default defineComponent({
data() {
let cols = _defaultColumn
const it = new InventoryTable(SampleData.aysheBoyfriend,
...cols
);
const dat = it.BuildTable()
return {
hotSettings: {
data: dat.data,
...dat.settings,
}
};
},
methods: {
swapHotData: function(event:any) {
let trm = event.target.value.split(",").map((x:any)=>x.trim())
let cols = ColumnByNames(...(trm as any)).filter(x=>x!=undefined)
if(cols.length < 2) {
cols = _defaultColumn
}
const it = new InventoryTable(SampleData.aysheBoyfriend,
...cols)
const hot = ((this.$refs!.hotTableComponent as any).hotInstance as Handsontable)
hot.updateSettings(it.BuildTable().settings)
}
},
components: {
HotTable,
}
});
</script> </script>
<style src="handsontable/dist/handsontable.full.css"></style> <script lang="ts">
import { defineComponent, computed, PropType, defineProps, defineEmits, watch} from 'vue';
import { registerAllModules } from 'handsontable/registry';
import { DefaultSettings, InventoryTable, TableRecipe } from '../lib/table';
import { Columns, ColumnByNames, ColumnInfo } from '../lib/columns';
import { TricksterItem, SampleData } from '../lib/trickster';
import Handsontable from 'handsontable';
import { useStoreRef } from '../state/state';
import { storage } from '../session_storage';
import { LTOApi, LTOApiv0 } from '../lib/lifeto';
</script>
<style src="handsontable/dist/handsontable.full.css">
</style>

View File

@ -0,0 +1,42 @@
<template>
<div v-for="v in characters">
<CharacterCard :character="v" />
</div>
</template>
<script lang="ts" setup>
import CharacterCard from './CharacterCard.vue';
const { accounts, invs } = useStoreRef()
const characters = ref([] as string[])
watch(invs, () => {
characters.value = [...new Set([...characters.value, ...invs.value.keys()])]
}, { deep: true })
const session = storage.GetSession()
const api: LTOApi = new LTOApiv0(session)
api.GetAccounts().then(xs => {
xs.forEach(x => {
accounts.value.add(x)
api.GetCharacters(x).then(chars => {
chars.forEach(char => {
log.debug("new character", char)
invs.value.set(char.path, char)
})
})
})
})
</script>
<script lang="ts">
import { defineComponent, computed, PropType, defineProps, defineEmits, ref, watch } from 'vue';
import { LTOApi, LTOApiv0 } from '../lib/lifeto';
import { LoginHelper, Session } from '../lib/session';
import { storage } from '../session_storage';
import { useStore, useStoreRef } from '../state/state';
import log from 'loglevel';
</script>

View File

@ -0,0 +1,35 @@
<template>
<input type="checkbox" id={{props.colname}} v-model="checked" />
<label for={{props.colname}}>{{(props.label ? props.label : Columns[props.colname].displayName)}}</label>
</template>
<script lang="ts" setup>
const props = defineProps(["colname","label"])
const {columns} = useStoreRef()
const checked = ref(columns.value.has(props.colname))
watch(columns.value.dirty,()=>{
console.log("changed")
if(columns.value.has(props.colname)) {
checked.value = true
}else{
checked.value = false
}
},{deep:true})
watch(checked, ()=>{
if(checked.value === true) {
columns.value.add(props.colname)
return
}
if(checked.value === false) {
columns.value.delete(props.colname)
return
}
})
</script>
<script lang="ts">
import { defineProps, ref, watch } from 'vue';
import { useStoreRef } from '../state/state';
import { ColumnName, Columns } from '../lib/columns';
</script>

View File

@ -0,0 +1,124 @@
<template>
<input
type="checkbox"
class="css-checkbox"
:id="'toggle-'+props.header"
v-model="show"
/>
<label :for="'toggle-'+props.header" class="css-label">
<span class="fa fa-plus">+</span>
<span class="fa fa-minus">-</span>
</label>
<label :for="'checkbox-'+props.header">{{props.header}}</label>
<input type="checkbox" :id="'checkbox-'+props.header" v-model="checked" />
<br>
<div
class="checkbox_parent"
:style="'grid-template-rows: repeat('+Math.ceil(props.columns.length/2+2)+', 1fr);'"
v-if="show"
>
<div
v-for="(item, index) in props.columns"
class="checkbox_child"
>
<ColumnCheckbox :colname="item"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import ColumnCheckbox from './ColumnCheckbox.vue';
const props = defineProps<{
header: string
columns: ColumnName[]
default?: boolean
}>()
const {columns} = useStoreRef()
const checked = ref(props.default)
const show = ref(true)
watch(show,()=>{
})
watch(checked,()=>{
if(checked.value === true) {
props.columns.forEach(x=>columns.value.add(x))
return
}
if(checked.value === false) {
props.columns.forEach(x=>columns.value.delete(x))
return
}
})
</script>
<script lang="ts">
import { defineProps, ref, watch } from 'vue';
import { useStoreRef } from '../state/state';
import { ColumnName } from '../lib/columns';
</script>
<style>
.checkbox_parent {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 0px;
grid-row-gap: 0px;
}
.checkbox_child {
align-content: left;
justify-self: left;
}
.css-label {
cursor: pointer;
}
.css-checkbox {
display: none;
}
.fa {
color: white;
border-radius: 4px;
padding-top: 0px;
padding-right: 4px;
padding-left: 4px;
padding-bottom: 0px;
}
.fa-plus {
background-color: #E85764;
}
.fa-minus {
background-color: #3AC5C9;
display: none;
}
.css-checkbox:checked + .css-label .fa-minus {
display: inline;
}
.css-checkbox:checked + .css-label .fa-plus {
display: none;
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<input type="checkbox" id={{props.colname}} v-model="checked" />
<label for={{props.colname}}>{{(props.label ? props.label : Columns[props.colname].displayName)}}</label>
</template>
<script lang="ts" setup>
const props = defineProps(["colname","label"])
const {tags} = useStoreRef()
const checked = ref(tags.value.has(props.colname))
watch(tags.value.dirty,()=>{
console.log("changed")
if(tags.value.has(props.colname)) {
checked.value = true
}else{
checked.value = false
}
},{deep:true})
watch(checked, ()=>{
if(checked.value === true) {
tags.value.add(props.colname)
return
}
if(checked.value === false) {
tags.value.delete(props.colname)
return
}
})
</script>
<script lang="ts">
import { defineProps, ref, watch } from 'vue';
import { useStoreRef } from '../state/state';
import { ColumnName, Columns } from '../lib/columns';
</script>

View File

@ -0,0 +1,123 @@
<template>
<input
type="checkbox"
class="css-checkbox"
:id="'toggle-'+props.header"
v-model="show"
/>
<label :for="'toggle-'+props.header" class="css-label">
<span class="fa fa-plus">+</span>
<span class="fa fa-minus">-</span>
</label>
<label :for="'checkbox-'+props.header">{{props.header}}</label>
<input type="checkbox" :id="'checkbox-'+props.header" v-model="checked" />
<br>
<div
class="checkbox_parent"
:style="'grid-template-rows: repeat('+Math.ceil(props.columns.length/2+2)+', 1fr);'"
v-if="show"
>
<div
v-for="(item, index) in props.columns"
class="checkbox_child"
>
<FilterCheckbox :colname="item"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import FilterCheckbox from './FilterCheckbox.vue';
const props = defineProps<{
header: string
columns: ColumnName[]
}>()
const {tags} = useStoreRef()
const checked = ref(false)
const show = ref(true)
watch(show,()=>{
})
watch(checked,()=>{
if(checked.value === true) {
props.columns.forEach(x=>tags.value.add(x))
return
}
if(checked.value === false) {
props.columns.forEach(x=>tags.value.delete(x))
return
}
})
</script>
<script lang="ts">
import { defineProps, ref, watch } from 'vue';
import { useStoreRef } from '../state/state';
import { ColumnName } from '../lib/columns';
</script>
<style>
.checkbox_parent {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 0px;
grid-row-gap: 0px;
}
.checkbox_child {
align-content: left;
justify-self: left;
}
.css-label {
cursor: pointer;
}
.css-checkbox {
display: none;
}
.fa {
color: white;
border-radius: 4px;
padding-top: 0px;
padding-right: 4px;
padding-left: 4px;
padding-bottom: 0px;
}
.fa-plus {
background-color: #E85764;
}
.fa-minus {
background-color: #3AC5C9;
display: none;
}
.css-checkbox:checked + .css-label .fa-minus {
display: inline;
}
.css-checkbox:checked + .css-label .fa-plus {
display: none;
}
</style>

View File

@ -1,24 +0,0 @@
<template>
<button @click="refreshSessions">
Refresh
</button>
</template>
<script lang="ts">
import { defineComponent, computed, PropType, defineProps, defineEmits} from 'vue';
import { Session } from '../lib/session';
import { storage } from '../session_storage';
export default defineComponent({
data() {
return {
Sessions: {} as any,
};
},
methods: {
refreshSessions: function(event:any) {
this.Sessions = storage.GetSessions()
},
},
});
</script>

View File

@ -0,0 +1,28 @@
<template>
Filters:
<br>
<FilterCheckboxGroup :header="'tags:'" :columns="TagColumns" />
<br>
<br>
Columns:
<br>
<ColumnCheckboxGroup :header="'action:'" :columns="MoveColumns" :default="true" />
<ColumnCheckboxGroup :header="'details:'" :columns="DetailsColumns" :default="true"/>
<ColumnCheckboxGroup :header="'equipment:'" :columns="EquipmentColumns" />
<ColumnCheckboxGroup :header="'stats:'" :columns="StatsColumns" />
</template>
<script lang="ts" setup>
import ColumnCheckboxGroup from './ColumnCheckboxGroup.vue';
import FilterCheckboxGroup from './FilterCheckboxGroup.vue';
import { StatsColumns, MoveColumns, TagColumns, EquipmentColumns, DetailsColumns } from '../lib/columns';
const { accounts, invs } = useStoreRef()
</script>
<script lang="ts">
import { useStoreRef } from '../state/state';
</script>

View File

@ -5,13 +5,62 @@ import { TricksterInventory, TricksterItem } from "./trickster"
import Core from "handsontable/core"; import Core from "handsontable/core";
type DefaultColumnName = "Image"|"Name"|"Count" export const BasicColumns = [
type ActionColumnName = "Move"|"MoveCount"|"ConfirmMove" "Image","Name","Count",
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 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 { export interface ColumnInfo {
name: ColumnName 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 { class Equip implements ColumnInfo {
name:ColumnName = "Equip" name:ColumnName = "Equip"
displayName = "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 { class Card implements ColumnInfo {
name:ColumnName = "Card" name:ColumnName = "Card"
displayName = "card" displayName = "card"
@ -202,7 +250,7 @@ class AP implements ColumnInfo {
} }
class GunAP implements ColumnInfo { class GunAP implements ColumnInfo {
name:ColumnName = "Gun AP" name:ColumnName = "GunAP"
displayName = "Gun AP" displayName = "Gun AP"
getter(item:TricksterItem):(string|number){ getter(item:TricksterItem):(string|number){
return item.stats ? item.stats["Gun AP"] : "" return item.stats ? item.stats["Gun AP"] : ""
@ -301,7 +349,7 @@ class MinLvl implements ColumnInfo {
displayName = "lvl" displayName = "lvl"
getter(item:TricksterItem):(string|number){ getter(item:TricksterItem):(string|number){
//TODO: //TODO:
return 0 return item.item_min_level? item.item_min_level:""
} }
} }
@ -310,13 +358,13 @@ class Slots implements ColumnInfo {
displayName = "slots" displayName = "slots"
getter(item:TricksterItem):(string|number){ getter(item:TricksterItem):(string|number){
//TODO: //TODO:
return 0 return item.item_slots ? item.item_slots : ""
} }
} }
class RefineNumber implements ColumnInfo { class RefineNumber implements ColumnInfo {
name:ColumnName = "RefineNumber" name:ColumnName = "RefineNumber"
displayName = "R#" displayName = "refine"
getter(item:TricksterItem):(string|number){ getter(item:TricksterItem):(string|number){
return item.refine_level ? item.refine_level : 0 return item.refine_level ? item.refine_level : 0
} }
@ -324,31 +372,84 @@ class RefineNumber implements ColumnInfo {
class RefineState implements ColumnInfo { class RefineState implements ColumnInfo {
name:ColumnName = "RefineState" name:ColumnName = "RefineState"
displayName = "State" displayName = "bork"
getter(item:TricksterItem):(string|number){ getter(item:TricksterItem):(string|number){
return item.refine_state ? item.refine_state : 0 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[]) => { export const ColumnByNames = (...n:ColumnName[]) => {
return n.map(ColumnByName) return n.map(ColumnByName)
} }
export const ColumnByName = (n:ColumnName) => { export const ColumnByName = (n:ColumnName) => {
return Columns[n] return Columns[n]
} }
export const Columns:{[Property in ColumnName]:ColumnInfo}= {
export const Columns = { Use: new Use(),
Desc: new Desc(),
Image: new Image(), Image: new Image(),
Name: new Name(), Name: new Name(),
Count: new Count(), Count: new Count(),
Move: new Move(), Move: new Move(),
MoveCount: new MoveCount(), MoveCount: new MoveCount(),
ConfirmMove: new ConfirmMove(),
Equip: new Equip(), Equip: new Equip(),
Drill: new Drill(), Drill: new Drill(),
Card: new Card(), Card: new Card(),
@ -356,7 +457,6 @@ export const Columns = {
Consume: new Consume(), Consume: new Consume(),
AP: new AP(), AP: new AP(),
GunAP: new GunAP(), GunAP: new GunAP(),
'Gun AP': new GunAP(),
AC: new AC(), AC: new AC(),
DX: new DX(), DX: new DX(),
MP: new MP(), MP: new MP(),
@ -372,33 +472,5 @@ export const Columns = {
Slots: new Slots(), Slots: new Slots(),
RefineNumber: new RefineNumber(), RefineNumber: new RefineNumber(),
RefineState: new RefineState(), RefineState: new RefineState(),
image: new Image(), All: new All(),
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(),
} }

105
src/lib/lifeto.ts Normal file
View File

@ -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<Array<TricksterInventory>>
GetAccounts:() =>Promise<Array<string>>
GetLoggedin: ()=>Promise<boolean>
}
export class LTOApiv0 implements LTOApi {
s: Session
constructor(s:Session) {
this.s = s
}
GetCharacters = async (account: string):Promise<Array<TricksterInventory>> =>{
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<TricksterInventory> =>{
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<string[]> => {
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<boolean> => {
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
})
}
}

View File

@ -1,4 +1,4 @@
import axios from "axios"; import axios, { AxiosResponse, Method } from "axios";
import qs from "qs"; import qs from "qs";
import { getCookie, removeCookie } from "typescript-cookie"; import { getCookie, removeCookie } from "typescript-cookie";
@ -6,15 +6,20 @@ export interface Session {
user:string user:string
xsrf:string xsrf:string
csrf:string csrf:string
lto:string authed_request:(verb:Method,url:string,data:any)=>Promise<any>
} }
export const API_ROOT = "/lifeto/" export const SITE_ROOT = "/lifeto/"
export const API_ROOT = "api/lifeto/"
const endpoint = (name:string)=>{ const login_endpoint = (name:string)=>{
return API_ROOT + name return SITE_ROOT + name
} }
const api_endpoint = (name:string)=>{
return SITE_ROOT+API_ROOT + name
}
export class LoginHelper { export class LoginHelper {
user:string user:string
@ -26,9 +31,8 @@ export class LoginHelper {
} }
login = async ():Promise<TokenSession> =>{ login = async ():Promise<TokenSession> =>{
removeCookie("XSRF-TOKEN") removeCookie("XSRF-TOKEN")
removeCookie("lifeto_session", {path:'/'})
await sleep(1000) await sleep(1000)
return axios.get(endpoint("login"),{withCredentials:false}) return axios.get(login_endpoint("login"),{withCredentials:false})
.then(async (x)=>{ .then(async (x)=>{
console.log(x) console.log(x)
if(x.data){ if(x.data){
@ -37,7 +41,7 @@ export class LoginHelper {
}catch(e){ }catch(e){
} }
} }
return axios.post(endpoint("login"),{ return axios.post(login_endpoint("login"),{
login:this.user, login:this.user,
password:this.pass, password:this.pass,
redirectTo:"lifeto" redirectTo:"lifeto"
@ -45,41 +49,38 @@ export class LoginHelper {
.then(async (x)=>{ .then(async (x)=>{
await sleep(100) await sleep(100)
let xsrf= getCookie("XSRF-TOKEN") let xsrf= getCookie("XSRF-TOKEN")
let lifeto = getCookie("lifeto_session") return new TokenSession(this.user,this.csrf!, xsrf!)
return new TokenSession(this.user,this.csrf!, xsrf!, lifeto!)
}) })
}) })
} }
} }
const sleep= async(ms:number)=> { const sleep= async(ms:number)=> {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
export class TokenSession {
export class TokenSession implements Session {
csrf:string csrf:string
xsrf:string xsrf:string
lto:string
user:string user:string
constructor(name:string, csrf:string, xsrf: string, lifeto:string){ constructor(name:string, csrf:string, xsrf: string){
this.user = name this.user = name
this.csrf = csrf this.csrf = csrf
this.xsrf = xsrf; 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<AxiosResponse> => {
let promise let promise
switch (verb){ switch (verb.toLowerCase()){
case "post": case "post":
promise = axios.post(endpoint(url),data,this.genHeaders()) promise = axios.post(api_endpoint(url),data,this.genHeaders())
break; break;
case "postraw": case "postraw":
const querystring = qs.stringify(data) const querystring = qs.stringify(data)
promise = axios.post(endpoint(url),querystring,this.genHeaders()) promise = axios.post(api_endpoint(url),querystring,this.genHeaders())
break; break;
case "get": case "get":
default: default:
promise = axios.get(endpoint(url),this.genHeaders()) promise = axios.get(api_endpoint(url),this.genHeaders())
} }
return promise.then(x=>{ return promise.then(x=>{
if(x.data){ if(x.data){
@ -93,7 +94,6 @@ export class TokenSession {
return y.split("=")[1].split(";")[0]; return y.split("=")[1].split(";")[0];
}) })
this.xsrf = cookies[0] this.xsrf = cookies[0]
this.lto = cookies[1]
} }
return x return x
}) })
@ -101,19 +101,8 @@ export class TokenSession {
genHeaders = ()=>{ genHeaders = ()=>{
const out = { const out = {
headers:{ headers:{
Cookie:`XSRF-TOKEN=${this.xsrf}; lifeto_session=${this.lto}`, Accept: "application/json",
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",
"Update-Insecure-Requests": 1, "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 withCredentials:true
} }

View File

@ -1,46 +1,137 @@
import { HotTableProps } from "@handsontable/vue3/types" import { HotTableProps } from "@handsontable/vue3/types"
import { TricksterInventory } from "./trickster" import { TricksterInventory } from "./trickster"
import {ColumnInfo} from "./columns" import {ColumnInfo, ColumnName, Columns, ColumnSorter, LazyColumn} from "./columns"
import { ColumnSettings } from "handsontable/settings" import { ColumnSettings } from "handsontable/settings"
import { PredefinedMenuItemKey } from "handsontable/plugins/contextMenu" import { PredefinedMenuItemKey } from "handsontable/plugins/contextMenu"
import { ref } from "vue"
export interface InventoryTableOptions {
columns: ColumnSet
tags: ColumnSet
}
export interface Mappable<T> {
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
}
export class ColumnSet implements Set<ColumnInfo>, Mappable<ColumnInfo>{
s: Set<ColumnName> = new Set()
size: number;
dirty = ref(0)
constructor(i?:Iterable<ColumnInfo | ColumnName>){
if(i){
for (const a of i) {
this.s.add(LazyColumn(a))
}
}
this.size = 0
this.mark()
}
map<U>(callbackfn: (value: ColumnInfo, index: number, array: ColumnInfo[]) => U, thisArg?: any): U[] {
return Array.from(this.values()).map(callbackfn, thisArg)
}
[Symbol.iterator](): IterableIterator<ColumnInfo>{
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<ColumnInfo>{
return this.values()
}
forEach(callbackfn: (value: ColumnInfo, value2: ColumnInfo, set: Set<ColumnInfo>) => void, thisArg?: any): void{
Array.from(this.values()).forEach((v)=>{
if(this.has(v)) {
callbackfn(v, v, new Set(this.values()))
}
}, thisArg)
}
values(): IterableIterator<ColumnInfo>{
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 { export class InventoryTable {
inventory:TricksterInventory inv!:TricksterInventory
columns: ColumnInfo[] o!: InventoryTableOptions
constructor(inv:TricksterInventory, ...columns:ColumnInfo[]) { constructor(inv:TricksterInventory, o:InventoryTableOptions) {
this.inventory = inv this.setInv(inv)
this.columns = columns this.setOptions(o)
}
setOptions(o:InventoryTableOptions) {
this.o = o
}
setInv(inv:TricksterInventory) {
this.inv = inv
} }
getTableColumnNames(): string[] { getTableColumnNames(): string[] {
return this.columns.map(x=>x.displayName) return this.o.columns.map(x=>x.displayName)
} }
getTableColumnSettings(): ColumnSettings[] { getTableColumnSettings(): ColumnSettings[] {
return this.columns.map(x=>({ return this.o.columns.map(x=>({
renderer: x.renderer ? x.renderer : "text", renderer: x.renderer ? x.renderer : "text",
dropdownMenu: x.filtering ? DefaultDropdownItems() : false, dropdownMenu: x.filtering ? DefaultDropdownItems() : false,
readOnly: x.writable ? false : true, readOnly: x.writable ? false : true,
selectionMode: x.writable ? "multiple" : "single" selectionMode: x.writable ? "multiple" : "single"
})) }))
} }
getTableRows():any[][] { getTableRows():any[][] {
return this.inventory.items.map((item)=>{ return Object.values(this.inv.items)
return this.columns.map(x=>x.getter(item)) .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 { BuildTable():TableRecipe {
const s = DefaultSettings() const s = DefaultSettings()
const dat = this.getTableRows()
return { return {
data: this.getTableRows(), data: dat,
settings: { settings: {
data: this.getTableRows(), data: dat,
colHeaders:this.getTableColumnNames(), colHeaders:this.getTableColumnNames(),
columns:this.getTableColumnSettings(), columns:this.getTableColumnSettings(),
...s ...s
@ -71,6 +162,7 @@ export const DefaultSettings = ():HotTableProps=>{
indicator: true, indicator: true,
headerAction: true, headerAction: true,
}, },
className: 'htLeft',
contextMenu: false, contextMenu: false,
readOnlyCellClassName: "", readOnlyCellClassName: "",
licenseKey:"non-commercial-and-evaluation", licenseKey:"non-commercial-and-evaluation",

View File

@ -11,6 +11,10 @@ export interface TricksterItem {
item_name: string; item_name: string;
item_id: number; item_id: number;
item_count: number; item_count: number;
item_desc: string;
item_use: string;
item_slots?: number;
item_min_level?: number;
is_equip?: boolean; is_equip?: boolean;
is_drill?: boolean; is_drill?: boolean;
item_expire_time?: ItemExpireTime; item_expire_time?: ItemExpireTime;
@ -29,9 +33,10 @@ export interface TricksterWallet {
export interface TricksterInventory { export interface TricksterInventory {
name:string name:string
path:string
id:string
wallet?:TricksterWallet wallet?:TricksterWallet
items:TricksterItem[] items:{[key:string]:TricksterItem}
} }
@ -39,14 +44,11 @@ export interface TricksterInventory {
export const SampleData:{[key:string]:TricksterInventory} = { export const SampleData:{[key:string]:TricksterInventory} = {
aysheBoyfriend: { aysheBoyfriend: {
name: sampleAyshe.characters[100047311].name, name: sampleAyshe.characters[100047311].name,
items: Object.entries(sampleAyshe.items).map(([k, v]:[string, any])=>{ items: sampleAyshe.items,
v.unique_id = k
return v
}),
wallet: { wallet: {
galders: sampleAyshe.characters[100047311].galders, galders: sampleAyshe.characters[100047311].galders,
job_img: sampleAyshe.characters[100047311].job_img, job_img: sampleAyshe.characters[100047311].job_img,
state: sampleAyshe.characters[100047311].state state: sampleAyshe.characters[100047311].state
} }
} } as any
} }

View File

@ -3,6 +3,8 @@ import App from './App.vue'
import { globalCookiesConfig } from "vue3-cookies"; import { globalCookiesConfig } from "vue3-cookies";
import { createPinia } from 'pinia';
import log from 'loglevel';
globalCookiesConfig({ globalCookiesConfig({
expireTimes: "365d", expireTimes: "365d",
@ -12,4 +14,9 @@ globalCookiesConfig({
sameSite: "None", sameSite: "None",
}); });
createApp(App).mount('#app') log.setLevel("debug")
const pinia = createPinia()
createApp(App).
use(pinia).
mount('#app')

View File

@ -1,4 +1,5 @@
<template> <template>
<div> {{loginString}} </div>
<section class="login_modal"> <section class="login_modal">
<div class="login_field"> <div class="login_field">
<label for="userName">Username: </label> <label for="userName">Username: </label>
@ -28,39 +29,47 @@
</section> </section>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useVModel } from '@vueuse/core' const username = ref("")
const password = ref("")
const loginString = ref("not logged in")
const props = defineProps<{ const login = () => {
username:string new LoginHelper(username.value, password.value).login()
password:string .then((session)=>{
}>() console.log(session, "adding to storage")
const data = { storage.AddSession(session)
username: useVModel(props, 'username', defineEmits(['update:username'])), }).catch((e)=>{
password:useVModel(props, 'password', defineEmits(['update:password'])) if(e.code == "ERR_BAD_REQUEST") {
alert("invalid username/password")
return
}
alert("unknown error, please report")
console.log(e)
})
} }
const s = storage.GetSession()
const api = new LTOApiv0(s)
if (s != undefined) {
username.value = s.user
}
const updateLogin = () => {
api.GetLoggedin().then((res)=>{
if(res) {
loginString.value = "logged in as " + s.user
}
})
}
updateLogin()
</script> </script>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed, PropType, defineProps, defineEmits} from 'vue'; import { defineComponent, computed, PropType, defineProps, defineEmits, ref} from 'vue';
import { 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';
export default defineComponent({
methods: {
login: function() {
new LoginHelper(this.username, this.password).login()
.then((session)=>{
console.log(session, "adding to storage")
storage.AddSession(session)
}).catch((e)=>{
if(e.code == "ERR_BAD_REQUEST") {
alert("invalid username/password")
return
}
alert("unknown error, please report")
console.log(e)
})
},
},
});
</script> </script>

View File

@ -1,5 +1,5 @@
import { getCookie, setCookie} from 'typescript-cookie' 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 { export class Storage {
GetSessions():{[key:string]:Session} {
const all_accounts = getCookie(nameCookie("all_accounts")) GetSession():Session {
const accounts = all_accounts?.split(",") const {user, xsrf, csrf} = {
let out:{[key:string]:Session} = {}; user: getCookie(nameCookie("user"))!,
for (const account in accounts) { xsrf: getCookie(nameCookie("xsrf"))!,
let tmp = { csrf: getCookie(nameCookie("csrf"))!
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)
} }
return new TokenSession(user, xsrf, csrf)
} }
RemoveSession(s:Session) { RemoveSession() {
const all_accounts = getCookie(nameCookie("all_accounts")) setCookie(nameCookie("user"),"")
let accounts = all_accounts?.split(",") setCookie(nameCookie("xsrf"),"")
accounts = accounts ? accounts : [] setCookie(nameCookie("csrf"),"")
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),"")
} }
AddSessions(...s:Session[]) {
for(const v of s) {
this.AddSession(v)
}
}
AddSession(s:Session) { AddSession(s:Session) {
const all_accounts = getCookie(nameCookie("all_accounts")) setCookie(nameCookie("user"),s.user)
let accounts = all_accounts?.split(",") setCookie(nameCookie("xsrf"),s.xsrf)
accounts = accounts ? accounts : [] setCookie(nameCookie("csrf"),s.csrf)
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)
} }
} }

25
src/state/state.ts Normal file
View File

@ -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<string>,
invs: new Map() as Map<string,TricksterInventory>,
activeTable: "none",
screen: "default",
columns: new ColumnSet(_defaultColumn),
tags: new ColumnSet(),
}
}
})
export const useStoreRef = ()=>{return storeToRefs(useStore())};

View File

@ -12,10 +12,6 @@ export default defineConfig({
target: "https://beta.lifeto.co/", target: "https://beta.lifeto.co/",
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/lifeto/, ''), rewrite: (path) => path.replace(/^\/lifeto/, ''),
hostRewrite: "localhost:3001/lifeto",
cookieDomainRewrite:{
"*":"",
},
}, },
} }
} }