diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b8bdfe..1387ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.18.0 + +- Added new grouping options to the package list + ## 0.17.0 - Added a package creation prompt diff --git a/rust/tauri.conf.json b/rust/tauri.conf.json index 182c836..07c52d0 100644 --- a/rust/tauri.conf.json +++ b/rust/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "STARTLINER", - "version": "0.17.0", + "version": "0.18.0", "identifier": "zip.patafour.startliner", "build": { "beforeDevCommand": "bun run dev", diff --git a/src/components/ModList.vue b/src/components/ModList.vue index 417844c..34cedd3 100644 --- a/src/components/ModList.vue +++ b/src/components/ModList.vue @@ -4,13 +4,15 @@ import Button from 'primevue/button'; import Dialog from 'primevue/dialog'; import Fieldset from 'primevue/fieldset'; import InputText from 'primevue/inputtext'; +import MultiSelect from 'primevue/multiselect'; import SelectButton from 'primevue/selectbutton'; import ToggleSwitch from 'primevue/toggleswitch'; +import { emit } from '@tauri-apps/api/event'; import ModListEntry from './ModListEntry.vue'; import ModTitlecard from './ModTitlecard.vue'; import { invoke } from '../invoke'; import { useClientStore, usePkgStore, usePrfStore } from '../stores'; -import { Game, Package } from '../types'; +import { Feature, Game, Package } from '../types'; import { pkgKey } from '../util'; import { useI18n } from 'vue-i18n'; @@ -35,36 +37,105 @@ const loadPackages = () => { loadPackages(); -const group = computed(() => { - const grouped = Object.groupBy( - pkgs.allLocal - .filter((p) => gameSublist.value.includes(pkgKey(p))) - .filter( - (p) => - props.search === undefined || - p.name.toLowerCase().includes(props.search.toLowerCase()) || - p.namespace - .toLowerCase() - .includes(props.search.toLowerCase()) - ), - ({ namespace }) => namespace - ); - if (!('local' in grouped)) { - grouped['local'] = []; +const allCategories = computed(() => { + const res = new Set(); + for (const pkg of pkgs.allLocal) { + for (const cat of pkg.rmt?.categories ?? []) { + res.add(cat); + } } - const res: [string, Package[]][] = []; + return [...res.values()].sort((a, b) => a.localeCompare(b)); +}); + +const local = computed(() => { + return pkgs.allLocal + .filter((p) => gameSublist.value.includes(pkgKey(p))) + .filter((p) => p.namespace === 'local'); +}); + +const groupedList = computed(() => { + const searchedPkgs = pkgs.allLocal + .filter((p) => gameSublist.value.includes(pkgKey(p))) + .filter((p) => p.namespace !== 'local') + .filter( + (p) => + props.search === undefined || + p.name.toLowerCase().includes(props.search.toLowerCase()) || + p.namespace.toLowerCase().includes(props.search.toLowerCase()) + ); + + let grouped; + if (client.pkgListMode === 'namespace') { + grouped = Object.groupBy(searchedPkgs, ({ namespace }) => namespace); + } else if (client.pkgListMode === 'type') { + grouped = { + standard: [] as Package[], + native: [] as Package[], + segatools: [] as Package[], + unsupported: [] as Package[] | undefined, + }; + grouped.unsupported = []; + for (const pkg of searchedPkgs) { + const loc = pkg.loc; + if (!loc || !loc.status || typeof loc.status === 'string') { + grouped.unsupported.push(pkg); + } else { + if ( + loc.status.OK[0] & + (Feature.GameDLL | Feature.Mempatcher | Feature.AmdDLL) + ) { + grouped.native.push(pkg); + } else if (loc.status.OK[0] & Feature.Mod) { + grouped.standard.push(pkg); + } + if ( + loc.status.OK[0] & + (Feature.AMNet | + Feature.Aime | + Feature.ChuniIO | + Feature.ChusanHook | + Feature.Mu3IO | + Feature.Mu3Hook) + ) { + grouped.segatools.push(pkg); + } + } + } + if (grouped.unsupported.length === 0) { + delete grouped.unsupported; + } + } else { + grouped = {} as { [key: string]: Package[] }; + for (const pkg of searchedPkgs) { + for (const cat of pkg.rmt?.categories ?? []) { + if (client.hiddenCategories.includes(cat)) { + continue; + } + if (!(cat in grouped)) { + grouped[cat] = [] as Package[]; + } + grouped[cat].push(pkg); + } + } + } + + let res: [string, Package[]][] = []; for (const [k, v] of Object.entries(grouped)) { if (v !== undefined) { res.push([k, v]); } } - res.sort((a, b) => { - return a[0] === 'local' - ? -1000 - : b[0] === 'local' - ? 1000 - : `${a[0]}`.localeCompare(`${b[0]}`); - }); + + if ( + client.pkgListMode === 'namespace' || + client.pkgListMode === 'category' + ) { + res.sort((a, b) => `${a[0]}`.localeCompare(`${b[0]}`)); + } else if (client.pkgListMode === 'type') { + for (const entry of res) { + entry[0] = t(`pkglist.${entry[0]}`); + } + } return res; }); @@ -199,7 +270,43 @@ const gameModelChunithm = gameModel('chunithm'); /> -
+
+ + +
+ {{ t('pkglist.exclusions') }} +
+ +
+
-
+
+ +
+
-
-
diff --git a/src/components/ModListEntry.vue b/src/components/ModListEntry.vue index 2639429..fcee6cc 100644 --- a/src/components/ModListEntry.vue +++ b/src/components/ModListEntry.vue @@ -7,7 +7,7 @@ import LinkButton from './LinkButton.vue'; import ModTitlecard from './ModTitlecard.vue'; import UpdateButton from './UpdateButton.vue'; import { invoke } from '../invoke'; -import { usePkgStore, usePrfStore } from '../stores'; +import { useClientStore, usePkgStore, usePrfStore } from '../stores'; import { Feature, Package } from '../types'; import { hasFeature } from '../util'; import { useI18n } from 'vue-i18n'; @@ -16,6 +16,7 @@ const { t } = useI18n(); const prf = usePrfStore(); const pkgs = usePkgStore(); +const client = useClientStore(); const props = defineProps({ pkg: Object as () => Package, @@ -39,7 +40,13 @@ if (unsupported.value === true && model.value === true) {