diff --git a/.eslintrc.js b/.eslintrc.js index 1f5e344..d5074d2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -93,6 +93,7 @@ module.exports = { quotes: ['warn', 'single', { avoidEscape: true }], '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/strict-boolean-expressions': 'error', // allow debugger during development only 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', diff --git a/README.md b/README.md index 781b974..1d9d962 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ https://github.com/Yentis/osu-local-scores/releases ## Building ```bash yarn install -yarn dev::electron -yarn build::electron +yarn dev:electron +yarn build:electron ``` After building you will need to do the following steps (suggestions welcome): diff --git a/package.json b/package.json index 978f827..986a952 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "scripts": { "lint": "eslint --ext .js,.ts,.vue ./", "test": "echo \"No test specified\" && exit 0", - "dev::electron": "quasar dev -m electron", - "build::wasm": "cd src-wasm && cargo clippy && wasm-pack build", - "build::electron": "quasar build -m electron" + "dev:electron": "quasar dev -m electron", + "build:wasm": "cd src-wasm && cargo clippy && wasm-pack build", + "build:electron": "quasar build -m electron" }, "devDependencies": { "@babel/eslint-parser": "^7.13.14", diff --git a/src/components/ColumnBody.vue b/src/components/ColumnBody.vue index b2e90d6..afffaad 100644 --- a/src/components/ColumnBody.vue +++ b/src/components/ColumnBody.vue @@ -6,7 +6,7 @@ flat dense size="sm" - :icon="expand ? 'remove' : 'add'" + :icon="isExpanded ? 'remove' : 'add'" @click="expandRow" /> @@ -90,7 +90,7 @@ , required: true }, - expand: { - type: Boolean, + expanded: { + type: Object as PropType>, required: true } }, @@ -189,17 +189,31 @@ export default defineComponent({ const platformService = PlatformService() const getImageSrc = (beatmap: Beatmap) => { - return beatmap.filePath + return (beatmap.filePath !== undefined && beatmap.filePath.length > 0) ? `atom://${osuPath.value}/Songs/${beatmap.filePath}` : `https://b.ppy.sh/thumb/${beatmap.beatmapsetId}l.jpg` } + const getExpanded = (key: number) => { + return props.expanded.get(key) === true + } + + const key = ref(props.row.beatmap.beatmapId) const imageSrc = ref(getImageSrc(props.row.beatmap)) - watch(props, () => { imageSrc.value = getImageSrc(props.row.beatmap) }) + const isExpanded = ref(getExpanded(key.value)) + + watch(props, () => { + if (props.row.beatmap.beatmapId !== key.value) { + imageSrc.value = getImageSrc(props.row.beatmap) + } + key.value = props.row.beatmap.beatmapId + + isExpanded.value = getExpanded(key.value) + }) const onImageError = (beatmap: Beatmap) => { imageSrc.value = `https://b.ppy.sh/thumb/${beatmap.beatmapsetId}l.jpg` } const openURL = (url: string) => { platformService.openURL(url) } - const expandRow = () => { context.emit('update:expand') } + const expandRow = () => { context.emit('update:expand', props.row.beatmap.beatmapId) } return { visibleColumns, @@ -208,6 +222,7 @@ export default defineComponent({ onImageError, openURL, expandRow, + isExpanded, STATUS, UNSUBMITTED } diff --git a/src/components/SettingsDialog.vue b/src/components/SettingsDialog.vue index 1fc7cc0..665a9bc 100644 --- a/src/components/SettingsDialog.vue +++ b/src/components/SettingsDialog.vue @@ -99,7 +99,7 @@ export default defineComponent({ platformService.openFilePicker(osuPath.value) .then((result) => { const newOsuPath = result.filePaths[0] - if (!newOsuPath) return + if (newOsuPath === undefined || newOsuPath.length === 0) return osuPath.value = newOsuPath }) diff --git a/src/components/cells/Combo.vue b/src/components/cells/Combo.vue index 96b71de..3896618 100644 --- a/src/components/cells/Combo.vue +++ b/src/components/cells/Combo.vue @@ -7,15 +7,15 @@ v-if="score" class="column" > - {{ +score.combo.toFixed(2) }} + {{ formatCombo(score.combo) }} {{ +score.maxCombo.toFixed(2) }} + >{{ formatCombo(score.maxCombo) }} {{ formatAltCombo(score) }} @@ -41,6 +41,11 @@ export default defineComponent({ setup () { const { visibleColumns } = SettingsService() + const formatCombo = (combo?: number) => { + if (combo === undefined) return '' + return +combo.toFixed(2) + } + const formatAltCombo = (score: Score) => { if (MODES[score.gamemode] === MANIA) { const altCombo = score.count300 !== 0 ? +(score.countGeki / score.count300).toFixed(2) : '∞' @@ -55,7 +60,9 @@ export default defineComponent({ visibleColumns, MODES, MANIA, - formatAltCombo + formatCombo, + formatAltCombo, + hasMaxCombo: (score: Score) => score.maxCombo !== undefined } } }) diff --git a/src/components/cells/Misses.vue b/src/components/cells/Misses.vue index 52739ad..d58a5cb 100644 --- a/src/components/cells/Misses.vue +++ b/src/components/cells/Misses.vue @@ -3,7 +3,7 @@ v-if="visibleColumns.includes('misses')" class="text-center" > - {{ score?.misses !== undefined ? score.misses : '?' }} + {{ formatMisses(score?.misses) }} @@ -24,7 +24,16 @@ export default defineComponent({ setup () { const { visibleColumns } = SettingsService() - return { visibleColumns } + + const formatMisses = (misses?: number) => { + if (misses === undefined) return '?' + return misses + } + + return { + visibleColumns, + formatMisses + } } }) diff --git a/src/components/cells/Pp.vue b/src/components/cells/Pp.vue index 8985ba6..0f6472d 100644 --- a/src/components/cells/Pp.vue +++ b/src/components/cells/Pp.vue @@ -4,20 +4,20 @@ class="text-center" >
- {{ +score.pp.toFixed(2) }} + {{ formatPp(score?.pp) }} {{ +score.maxPp.toFixed(2) }} + >{{ formatPp(score?.maxPp) }} {{ score.maxPp !== 0 ? `${+((score.pp / score.maxPp) * 100).toFixed(2)}%` : '∞' }} + >{{ formatAltPp(score) }}
? @@ -40,7 +40,23 @@ export default defineComponent({ setup () { const { visibleColumns } = SettingsService() - return { visibleColumns } + + const formatPp = (pp?: number) => { + if (pp === undefined) return '' + return +pp.toFixed(2) + } + + const formatAltPp = (score?: Score) => { + if (score?.pp === undefined || score?.maxPp === undefined) return '' + return score.maxPp !== 0 ? `${+((score.pp / score.maxPp) * 100).toFixed(2)}%` : '∞' + } + + return { + visibleColumns, + isDefined: (input?: unknown) => input !== undefined, + formatPp, + formatAltPp + } } }) diff --git a/src/pages/Index.vue b/src/pages/Index.vue index b6f7617..67ba21c 100644 --- a/src/pages/Index.vue +++ b/src/pages/Index.vue @@ -22,8 +22,8 @@ @@ -113,35 +113,35 @@ export default defineComponent({ break } case COLUMNS.gamemode: { - doScoreSort(rows, (score) => score?.gamemode || 0, descending) + doScoreSort(rows, (score) => score?.gamemode !== undefined ? score.gamemode : 0, descending) break } case COLUMNS.score: { - doScoreSort(rows, (score) => score?.score || 0, descending) + doScoreSort(rows, (score) => score?.score !== undefined ? score.score : 0, descending) break } case COLUMNS.grade: { - doScoreSort(rows, (score) => score?.grade || 0, descending) + doScoreSort(rows, (score) => score?.grade !== undefined ? score.grade : 0, descending) break } case COLUMNS.accuracy: { - doScoreSort(rows, (score) => score?.accuracy || 0, descending) + doScoreSort(rows, (score) => score?.accuracy !== undefined ? score.accuracy : 0, descending) break } case COLUMNS.misses: { - doScoreSort(rows, (score) => score?.misses || 0, descending) + doScoreSort(rows, (score) => score?.misses !== undefined ? score.misses : 0, descending) break } case COLUMNS.combo: { - doScoreSort(rows, (score) => score?.combo || 0, descending) + doScoreSort(rows, (score) => score?.combo !== undefined ? score.combo : 0, descending) break } case COLUMNS.date: { - doScoreSort(rows, (score) => score?.date ? new Date(score.date).getTime() : 0, descending) + doScoreSort(rows, (score) => score?.date !== undefined ? new Date(score.date).getTime() : 0, descending) break } case COLUMNS.pp: { - doScoreSort(rows, (score) => score?.pp || 0, descending) + doScoreSort(rows, (score) => score?.pp !== undefined ? score.pp : 0, descending) break } } @@ -149,6 +149,15 @@ export default defineComponent({ return descending ? rows.reverse() : rows } + const expanded = ref(new Map()) + const expand = (key: number) => { + const map = expanded.value + const isExpanded = map.get(key) === true + + map.set(key, !isExpanded) + expanded.value = map + } + return { table, columns, @@ -156,7 +165,9 @@ export default defineComponent({ filter, openURL, visibleColumns, - doSort + doSort, + expanded, + expand } } }) diff --git a/src/router/index.ts b/src/router/index.ts index 11dd9e2..dd3c769 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -17,7 +17,8 @@ import routes from './routes' */ export default route(function (/* { store, ssrContext } */) { - const createHistory = process.env.SERVER + const server = (process.env.SERVER as unknown) as boolean + const createHistory = server ? createMemoryHistory : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory) diff --git a/src/services/UpdateService.ts b/src/services/UpdateService.ts index f8d7e83..2a1a843 100644 --- a/src/services/UpdateService.ts +++ b/src/services/UpdateService.ts @@ -28,7 +28,7 @@ export class UpdateService { if (update) { this.showUpdateAvailable(update) } const changelog = await this.getChangelog() - if (!changelog) return + if (changelog === undefined) return Dialog.create({ component: ConfirmationDialog, @@ -48,8 +48,9 @@ export class UpdateService { return githubReleases } - private getVersion () { - return LocalStorage.getItem(VERSION) || '' + private getVersion (): string { + const version = LocalStorage.getItem(VERSION) + return typeof version === 'string' ? version : '' } private async getChangelog (): Promise { diff --git a/src/worker/Worker.ts b/src/worker/Worker.ts index d20015f..354ca23 100644 --- a/src/worker/Worker.ts +++ b/src/worker/Worker.ts @@ -158,7 +158,7 @@ function filterBeatmaps (filter: Filter): DatabaseResponse[] { } // Max Combo - const maxCombo = score.maxCombo || 0 + const maxCombo = score.maxCombo !== undefined ? score.maxCombo : 0 const maxComboMin = typeof filter.maxComboMin === 'number' ? filter.maxComboMin : maxCombo const maxComboMax = typeof filter.maxComboMax === 'number' ? filter.maxComboMax : maxCombo if (maxCombo < maxComboMin || maxCombo > maxComboMax) { @@ -216,14 +216,14 @@ function filterBeatmaps (filter: Filter): DatabaseResponse[] { // Date const date = new Date(score.date).getTime() - const dateMin = filter.dateMin ? new Date(filter.dateMin).getTime() : date - const dateMax = filter.dateMax ? new Date(filter.dateMax).getTime() : date + const dateMin = filter.dateMin !== undefined ? new Date(filter.dateMin).getTime() : date + const dateMax = filter.dateMax !== undefined ? new Date(filter.dateMax).getTime() : date if (date < dateMin || date > dateMax) { return false } // PP - const pp = score.pp || 0 + const pp = score.pp !== undefined ? score.pp : 0 const ppMin = typeof filter.ppMin === 'number' ? filter.ppMin : pp const ppMax = typeof filter.ppMax === 'number' ? filter.ppMax : pp if (pp < ppMin || pp > ppMax) { @@ -231,7 +231,7 @@ function filterBeatmaps (filter: Filter): DatabaseResponse[] { } // Max PP - const maxPp = score.maxPp || 0 + const maxPp = score.maxPp !== undefined ? score.maxPp : 0 const maxPpMin = typeof filter.maxPpMin === 'number' ? filter.maxPpMin : maxPp const maxPpMax = typeof filter.maxPpMax === 'number' ? filter.maxPpMax : maxPp if (maxPp < maxPpMin || maxPp > maxPpMax) { @@ -305,7 +305,7 @@ async function calculatePpValues (osuPath: string, beatmaps: DatabaseResponse[]) } const filePath = beatmap.beatmap.filePath - if (!filePath) { + if (filePath === undefined || filePath.length === 0) { continue }