feat: map profiles, closes #868

This commit is contained in:
Alexander Drozdov
2023-02-01 01:00:39 +02:00
parent 6b4473b8d9
commit 4fe0eef295
6 changed files with 116 additions and 69 deletions
+11
View File
@@ -504,6 +504,17 @@ function upgradeConfig (_config: Config): Config {
const itemSearch = config.widgets.find(w => w.wmType === 'item-search') as widget.ItemSearchWidget
itemSearch.ocrGemsKey = null
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as widget.ItemCheckWidget
itemCheck.maps.profile = 1
for (const stat of itemCheck.maps.selectedStats) {
const p1decision =
(stat.decision === 'danger') ? 'd'
: (stat.decision === 'warning') ? 'w'
: (stat.decision === 'desirable') ? 'g' : 's'
stat.decision = `${p1decision}--`
}
config.configVersion = 15
}
+28 -6
View File
@@ -1,7 +1,18 @@
<template>
<div class="bg-gray-800 text-gray-200 border-gray-900 border-4"
style="min-width: 20rem; max-width: min(100vw - var(--game-panel), 30rem);">
<div class="bg-gray-900 py-1 px-4 text-center">{{ mapName }}</div>
<div class="bg-gray-900 py-1 px-8 flex items-baseline gap-2">
<div class="flex-1 text-center">{{ mapName }}</div>
<div class="ml-8 text-gray-400">{{ t('Profile') }}</div>
<div class="flex gap-0.5">
<button
v-for="profile in profiles" :key="profile.text"
@click="profile.select"
:class="{ 'border border-gray-600': profile.active }"
class="w-6 bg-gray-800"
>{{ profile.text }}</button>
</div>
</div>
<fullscreen-image v-if="image" :src="image" />
<div v-if="!mapStats.length" class="px-8 py-2">
{{ t('Item has no modifiers.') }}
@@ -41,12 +52,14 @@ export default defineComponent({
setup (props) {
const { t } = useI18n()
const config = computed(() => AppConfig<ItemCheckWidget>('item-check')!)
const config = computed(() => AppConfig<ItemCheckWidget>('item-check')!.maps)
const hasOutdatedTranslation = computed<boolean>(() => {
return config.value.maps.selectedStats
const idx = config.value.profile - 1
return config.value.selectedStats
.some(entry =>
entry.decision !== 'seen' &&
entry.decision[idx] !== '-' &&
entry.decision[idx] !== 's' &&
STAT_BY_MATCH_STR(entry.matcher) == null)
})
@@ -59,7 +72,15 @@ export default defineComponent({
: props.item.info.map?.screenshot
),
mapStats: computed(() => prepareMapStats(props.item)),
hasOutdatedTranslation
hasOutdatedTranslation,
profiles: computed(() => {
const ROMAN_NUMERALS = ['I', 'II', 'III']
return ROMAN_NUMERALS.map((text, i) => ({
text,
active: (config.value.profile === i + 1),
select: () => { config.value.profile = i + 1 }
}))
})
}
}
})
@@ -72,7 +93,8 @@ export default defineComponent({
},
"ru": {
"Item has no modifiers.": "На предмете нету модов.",
"has_outdated": "Перевод некоторых свойств был изменен. Проверьте и обновите опасные моды карт в настройках. (Это сообщение будет скрыто, как только вы удалите все устаревшие переводы)"
"has_outdated": "Перевод некоторых свойств был изменен. Проверьте и обновите опасные моды карт в настройках. (Это сообщение будет скрыто, как только вы удалите все устаревшие переводы)",
"Profile": "Профиль"
},
"cmn-Hant" :{
"has_outdataed" :"部份詞綴描述已變更,請檢查並更新地圖的危險詞綴。(當你刪除將所有舊版詞綴,此訊息會隱藏)"
+46 -46
View File
@@ -1,21 +1,12 @@
<template>
<div class="flex">
<button @click="handleClick"
class="px-2 leading-none py-2 m-0 text-left flex-1 flex items-center overflow-hidden"
:class="decision === 'danger' ? 'bg-red-700'
: decision === 'warning' ? 'bg-orange-600'
: decision === 'desirable' ? 'bg-green-700'
: 'hover:bg-gray-700'">
<template v-if="true">
<i v-if="!decision || decision === 'seen'"
class="inline-block mr-2" style="min-width: 1rem;">{{ '\u2009' }}</i>
<i v-else
class="fas mr-2 text-center" style="min-width: 1rem;"
:class="decision === 'danger' ? 'fa-skull-crossbones'
: decision === 'warning' ? 'fa-exclamation-triangle'
: 'fa-check'"></i>
</template>
<item-modifier-text :text="stat.matcher" :roll="stat.roll" class="truncate" />
class="p-2 gap-x-2 leading-none text-left flex-1 flex items-center overflow-hidden"
:class="btnStyle?.bg ?? 'hover:bg-gray-700'">
<i class="fas text-center w-4 shrink-0"
:class="btnStyle?.icon" />
<item-modifier-text class="truncate"
:text="stat.matcher" :roll="stat.roll" />
</button>
<button @click="toggleSeenStatus" class="flex leading-none items-center text-gray-600 w-8 text-center justify-center">
<i v-if="showNewStatIcon" class="fas fa-eye-slash"></i>
@@ -30,6 +21,12 @@ import { AppConfig } from '@/web/Config'
import { PreparedStat } from './prepare-map-stats'
import { ItemCheckWidget } from '../overlay/interfaces'
const BTN_STYLES = new Map([
['d', { bg: 'bg-red-700', icon: 'fa-skull-crossbones' }],
['w', { bg: 'bg-orange-600', icon: 'fa-exclamation-triangle' }],
['g', { bg: 'bg-green-700', icon: 'fa-check' }]
])
export default defineComponent({
components: { ItemModifierText },
props: {
@@ -39,48 +36,51 @@ export default defineComponent({
}
},
setup (props) {
const config = computed(() => AppConfig<ItemCheckWidget>('item-check')!)
const entry = computed(() => {
return config.value.maps.selectedStats.find(_ => _.matcher === props.stat.matcher)
const config = computed(() => AppConfig<ItemCheckWidget>('item-check')!.maps)
const entry = computed(() => config.value.selectedStats
.find(({ matcher }) => matcher === props.stat.matcher))
const decision = computed<string>({
get () {
if (!entry.value) return '-'
return entry.value.decision[config.value.profile - 1]
},
set (value) {
if (!entry.value) {
const decision = ['-', '-', '-']
decision[config.value.profile - 1] = value
config.value.selectedStats.push({
matcher: props.stat.matcher,
decision: decision.join('')
})
} else {
const decision = entry.value.decision.split('')
decision[config.value.profile - 1] = value
entry.value.decision = decision.join('')
}
}
})
const showNewStatIcon = computed(() => {
return config.value.maps.showNewStats && !entry.value
})
const showNewStatIcon = computed(() =>
config.value.showNewStats && decision.value === '-')
function toggleSeenStatus () {
if (!config.value.maps.showNewStats) return
if (!entry.value) {
config.value.maps.selectedStats.push({
matcher: props.stat.matcher,
decision: 'seen'
})
} else if (entry.value.decision === 'seen') {
config.value.maps.selectedStats = config.value.maps.selectedStats.filter(selected => selected !== entry.value)
if (config.value.showNewStats) {
decision.value = (decision.value === '-') ? 's' : '-'
}
}
return {
decision: computed(() => entry.value?.decision),
btnStyle: computed(() => BTN_STYLES.get(decision.value)),
showNewStatIcon,
toggleSeenStatus,
handleClick () {
if (!entry.value) {
config.value.maps.selectedStats.push({
matcher: props.stat.matcher,
decision: 'danger'
})
} else {
if (entry.value.decision === 'danger') {
entry.value.decision = 'warning'
} else if (entry.value.decision === 'warning') {
entry.value.decision = 'desirable'
} else if (entry.value.decision === 'desirable') {
entry.value.decision = 'seen'
} else if (entry.value.decision === 'seen') {
entry.value.decision = 'danger'
}
switch (decision.value) {
case '-': decision.value = 'd'; break
case 's': decision.value = 'd'; break
case 'd': decision.value = 'w'; break
case 'w': decision.value = 'g'; break
case 'g': decision.value = 's'; break
}
}
}
+1
View File
@@ -51,6 +51,7 @@ export interface ItemCheckWidget extends Widget {
craftOfExileKey: string | null
stashSearchKey: string | null
maps: {
profile: number
showNewStats: boolean
selectedStats: Array<{
matcher: string
@@ -6,9 +6,9 @@
<span class="truncate">{{ matcher.str }}</span>
</div>
<div class="flex items-baseline gap-x-4" :class="{ [$style['controls-auto-hide']]: !removable }">
<ui-radio v-model="decision" value="warning" class="p-1" />
<ui-radio v-model="decision" value="danger" class="p-1" />
<ui-radio v-model="decision" value="desirable" class="p-1" />
<ui-radio v-model="decision" value="w" class="p-1" />
<ui-radio v-model="decision" value="d" class="p-1" />
<ui-radio v-model="decision" value="g" class="p-1" />
<button v-if="removable" @click="remove"
class="flex items-center mx-1 py-1" :class="{ 'text-red-400': matcher.outdated }">
<i class="w-4" :class="matcher.outdated ? 'fas fa-trash-alt' : 'fas fa-times'"></i>
@@ -34,39 +34,47 @@ export default defineComponent({
selectedStats: {
type: Array as PropType<ItemCheckWidget['maps']['selectedStats']>,
required: true
},
profile: {
type: Number,
required: true
}
},
setup (props) {
const { t } = useI18n()
const entry = computed(() => {
return props.selectedStats.find(entry => entry.matcher === props.matcher.str)
})
const entry = computed(() => props.selectedStats
.find(({ matcher }) => matcher === props.matcher.str))
const decision = computed<undefined | string>({
const decision = computed<string>({
get () {
return entry.value?.decision
if (!entry.value) return '-'
return entry.value.decision[props.profile - 1]
},
set (value) {
if (entry.value) {
entry.value.decision = value!
} else {
if (!entry.value) {
const decision = ['-', '-', '-']
decision[props.profile - 1] = value
props.selectedStats.push({
matcher: props.matcher.str,
decision: value!
decision: decision.join('')
})
} else {
const decision = entry.value.decision.split('')
decision[props.profile - 1] = value
entry.value.decision = decision.join('')
}
}
})
function remove () {
if (entry.value) {
decision.value = 'seen'
decision.value = '---'
}
}
const removable = computed(() => {
return entry.value && decision.value !== 'seen'
return decision.value !== 's' && decision.value !== '-'
})
return {
+8 -3
View File
@@ -23,7 +23,8 @@
<maps-stat-entry
:style="{ position: 'absolute', top: `${props.top}px` }"
:matcher="props.item"
:selected-stats="selectedStats" />
:selected-stats="selectedStats"
:profile="profile" />
</virtual-scroll>
</div>
</template>
@@ -79,9 +80,10 @@ export default defineComponent({
})
const selectedMatchers = computed(() => {
const idx = widget.value.maps.profile - 1
return new Set(
widget.value.maps.selectedStats
.filter(entry => entry.decision !== 'seen')
.filter(({ decision }) => decision[idx] !== '-' && decision[idx] !== 's')
.map(entry => entry.matcher))
})
@@ -91,10 +93,12 @@ export default defineComponent({
})
const hasOutdatedTranslation = computed<MapStatMatcher[]>(() => {
const idx = widget.value.maps.profile - 1
return widget.value.maps
.selectedStats
.filter(entry =>
entry.decision !== 'seen' &&
entry.decision[idx] !== '-' &&
entry.decision[idx] !== 's' &&
STAT_BY_MATCH_STR(entry.matcher) == null)
.map(entry => ({ str: entry.matcher, heist: undefined, outdated: true }))
})
@@ -124,6 +128,7 @@ export default defineComponent({
]
}),
selectedStats: computed(() => widget.value.maps.selectedStats),
profile: computed(() => widget.value.maps.profile),
fontSize: computed(() => props.config.fontSize)
}
}